diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 5265ebb3f9..1695f77f84 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -8,8 +8,8 @@ body: value: | Thanks for taking the time to fill out this bug report! Please make sure to fill out the entire form below, providing as much context as you can in order to help us triage and track down your bug as quickly as possible. - Before filing a bug, please be sure you have searched through [existing bugs](https://github.com/open-telemetry/opentelemetry-python-contrib/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug) to see if your bug is already addressed. - + Before filing a bug, please be sure you have searched through [existing bugs](https://github.com/open-telemetry/opentelemetry-python-contrib/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug) to see if your bug is already addressed. + - type: textarea id: environment attributes: @@ -18,9 +18,9 @@ body: Please describe any aspect of your environment relevant to the problem, including your Python version, [platform](https://docs.python.org/3/library/platform.html), version numbers of installed dependencies, information about your cloud hosting provider, etc. If you're reporting a problem with a specific version of a library in this repo, please check whether the problem has been fixed on main. value: | OS: (e.g, Ubuntu) - Python version: (e.g., Python 3.9.10) + Python version: (e.g., Python 3.10.0) Package version: (e.g., 0.46.0) - + - type: textarea attributes: label: What happened? diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml new file mode 100644 index 0000000000..4a5b47acfe --- /dev/null +++ b/.github/workflows/check-links.yml @@ -0,0 +1,52 @@ +name: check-links +on: + push: + branches: [ main ] + paths: + - '**/*.md' + - '**/*.rst' + - '.github/workflows/check-links.yml' + - '.github/workflows/check_links_config.json' + pull_request: + paths: + - '**/*.md' + - '**/*.rst' + - '.github/workflows/check-links.yml' + - '.github/workflows/check_links_config.json' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + check-links: + runs-on: ubuntu-latest + if: ${{ github.actor != 'dependabot[bot]' && github.actor != 'otelbot[bot]' }} + timeout-minutes: 15 + steps: + - name: Checkout Repo + uses: actions/checkout@v6 + + - name: Get changed markdown files + id: changed-files + uses: tj-actions/changed-files@v46 + with: + files: | + **/*.md + **/*.rst + + - name: Install markdown-link-check + if: steps.changed-files.outputs.any_changed == 'true' + run: npm install -g markdown-link-check@v3.12.2 + + - name: Run markdown-link-check + if: steps.changed-files.outputs.any_changed == 'true' + run: | + markdown-link-check \ + --verbose \ + --config .github/workflows/check_links_config.json \ + ${{ steps.changed-files.outputs.all_changed_files }} \ + || { echo "Check that anchor links are lowercase"; exit 1; } diff --git a/.github/workflows/check_links_config.json b/.github/workflows/check_links_config.json new file mode 100644 index 0000000000..57f555a003 --- /dev/null +++ b/.github/workflows/check_links_config.json @@ -0,0 +1,14 @@ +{ + "ignorePatterns": [ + { + "pattern": "http(s)?://\\d+\\.\\d+\\.\\d+\\.\\d+" + }, + { + "pattern": "http(s)?://localhost" + }, + { + "pattern": "http(s)?://example.com" + } + ], + "aliveStatusCodes": [429, 200] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..a1963283de --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +# Do not edit this file. +# This file is generated automatically by executing tox -e generate-workflows + +name: CI + +on: + push: + branches: + - 'main' + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + misc: + uses: ./.github/workflows/misc.yml + lint: + uses: ./.github/workflows/lint.yml + tests: + uses: ./.github/workflows/test.yml + + check: + if: always() + needs: + - misc + - lint + - tests + runs-on: ubuntu-latest + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/core_contrib_test_0.yml b/.github/workflows/core_contrib_test.yml similarity index 67% rename from .github/workflows/core_contrib_test_0.yml rename to .github/workflows/core_contrib_test.yml index fa7f0d668b..d9f90d57d8 100644 --- a/.github/workflows/core_contrib_test_0.yml +++ b/.github/workflows/core_contrib_test.yml @@ -1,7 +1,7 @@ # Do not edit this file. # This file is generated automatically by executing tox -e generate-workflows -name: Core Contrib Test 0 +name: Core Contrib Test on: workflow_call: @@ -20,10 +20,14 @@ env: CORE_REPO_SHA: ${{ inputs.CORE_REPO_SHA }} CONTRIB_REPO_SHA: ${{ inputs.CONTRIB_REPO_SHA }} PIP_EXISTS_ACTION: w + CORE_REPO_API: ${{ github.workspace }}/opentelemetry-python/opentelemetry-api + CORE_REPO_SDK: ${{ github.workspace }}/opentelemetry-python/opentelemetry-sdk + CORE_REPO_SEMCONV: ${{ github.workspace }}/opentelemetry-python/opentelemetry-semantic-conventions + CORE_REPO_TEST_UTILS: ${{ github.workspace }}/opentelemetry-python/tests/opentelemetry-test-utils jobs: - py39-test-instrumentation-openai-v2-oldest: + py310-test-instrumentation-openai-v2-oldest: name: instrumentation-openai-v2-oldest runs-on: ubuntu-latest timeout-minutes: 30 @@ -41,19 +45,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-openai-v2-oldest -- -ra + run: tox -e py310-test-instrumentation-openai-v2-oldest -- -ra - py39-test-instrumentation-openai-v2-latest: + py310-test-instrumentation-openai-v2-latest: name: instrumentation-openai-v2-latest runs-on: ubuntu-latest timeout-minutes: 30 @@ -71,19 +75,79 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-openai-v2-latest -- -ra + run: tox -e py310-test-instrumentation-openai-v2-latest -- -ra - py39-test-instrumentation-vertexai-oldest: + py310-test-instrumentation-openai_agents-v2-oldest: + name: instrumentation-openai_agents-v2-oldest + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-openai_agents-v2-oldest -- -ra + + py310-test-instrumentation-openai_agents-v2-latest: + name: instrumentation-openai_agents-v2-latest + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-openai_agents-v2-latest -- -ra + + py310-test-instrumentation-vertexai-oldest: name: instrumentation-vertexai-oldest runs-on: ubuntu-latest timeout-minutes: 30 @@ -101,19 +165,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-vertexai-oldest -- -ra + run: tox -e py310-test-instrumentation-vertexai-oldest -- -ra - py39-test-instrumentation-vertexai-latest: + py310-test-instrumentation-vertexai-latest: name: instrumentation-vertexai-latest runs-on: ubuntu-latest timeout-minutes: 30 @@ -131,19 +195,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-vertexai-latest -- -ra + run: tox -e py310-test-instrumentation-vertexai-latest -- -ra - py39-test-instrumentation-google-genai-oldest: + py310-test-instrumentation-google-genai-oldest: name: instrumentation-google-genai-oldest runs-on: ubuntu-latest timeout-minutes: 30 @@ -161,19 +225,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-google-genai-oldest -- -ra + run: tox -e py310-test-instrumentation-google-genai-oldest -- -ra - py39-test-instrumentation-google-genai-latest: + py310-test-instrumentation-google-genai-latest: name: instrumentation-google-genai-latest runs-on: ubuntu-latest timeout-minutes: 30 @@ -191,19 +255,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-google-genai-latest -- -ra + run: tox -e py310-test-instrumentation-google-genai-latest -- -ra - py39-test-instrumentation-anthropic-oldest: + py310-test-instrumentation-anthropic-oldest: name: instrumentation-anthropic-oldest runs-on: ubuntu-latest timeout-minutes: 30 @@ -221,19 +285,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-anthropic-oldest -- -ra + run: tox -e py310-test-instrumentation-anthropic-oldest -- -ra - py39-test-instrumentation-anthropic-latest: + py310-test-instrumentation-anthropic-latest: name: instrumentation-anthropic-latest runs-on: ubuntu-latest timeout-minutes: 30 @@ -251,19 +315,79 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-anthropic-latest -- -ra + run: tox -e py310-test-instrumentation-anthropic-latest -- -ra + + py310-test-instrumentation-claude-agent-sdk-oldest: + name: instrumentation-claude-agent-sdk-oldest + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} - py39-test-resource-detector-containerid: + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-claude-agent-sdk-oldest -- -ra + + py310-test-instrumentation-claude-agent-sdk-latest: + name: instrumentation-claude-agent-sdk-latest + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-claude-agent-sdk-latest -- -ra + + py310-test-resource-detector-containerid: name: resource-detector-containerid runs-on: ubuntu-latest timeout-minutes: 30 @@ -281,19 +405,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-resource-detector-containerid -- -ra + run: tox -e py310-test-resource-detector-containerid -- -ra - py39-test-resource-detector-azure-0: + py310-test-resource-detector-azure-0: name: resource-detector-azure-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -311,19 +435,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-resource-detector-azure-0 -- -ra + run: tox -e py310-test-resource-detector-azure-0 -- -ra - py39-test-resource-detector-azure-1: + py310-test-resource-detector-azure-1: name: resource-detector-azure-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -341,19 +465,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-resource-detector-azure-1 -- -ra + run: tox -e py310-test-resource-detector-azure-1 -- -ra - py39-test-sdk-extension-aws-0: + py310-test-sdk-extension-aws-0: name: sdk-extension-aws-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -371,19 +495,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-sdk-extension-aws-0 -- -ra + run: tox -e py310-test-sdk-extension-aws-0 -- -ra - py39-test-sdk-extension-aws-1: + py310-test-sdk-extension-aws-1: name: sdk-extension-aws-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -401,19 +525,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-sdk-extension-aws-1 -- -ra + run: tox -e py310-test-sdk-extension-aws-1 -- -ra - py39-test-distro: + py310-test-distro: name: distro runs-on: ubuntu-latest timeout-minutes: 30 @@ -431,20 +555,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-distro -- -ra + run: tox -e py310-test-distro -- -ra - py39-test-opentelemetry-instrumentation: - name: opentelemetry-instrumentation + py310-test-opentelemetry-instrumentation-wrapt1: + name: opentelemetry-instrumentation-wrapt1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -461,19 +585,49 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-opentelemetry-instrumentation -- -ra + run: tox -e py310-test-opentelemetry-instrumentation-wrapt1 -- -ra + + py310-test-opentelemetry-instrumentation-wrapt2: + name: opentelemetry-instrumentation-wrapt2 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} - py39-test-instrumentation-aiohttp-client: + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-opentelemetry-instrumentation-wrapt2 -- -ra + + py310-test-instrumentation-aiohttp-client: name: instrumentation-aiohttp-client runs-on: ubuntu-latest timeout-minutes: 30 @@ -491,19 +645,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aiohttp-client -- -ra + run: tox -e py310-test-instrumentation-aiohttp-client -- -ra - py39-test-instrumentation-aiohttp-server: + py310-test-instrumentation-aiohttp-server: name: instrumentation-aiohttp-server runs-on: ubuntu-latest timeout-minutes: 30 @@ -521,20 +675,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aiohttp-server -- -ra + run: tox -e py310-test-instrumentation-aiohttp-server -- -ra - py39-test-instrumentation-aiopg: - name: instrumentation-aiopg + py310-test-instrumentation-aiopg-wrapt1: + name: instrumentation-aiopg-wrapt1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -551,19 +705,49 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aiopg -- -ra + run: tox -e py310-test-instrumentation-aiopg-wrapt1 -- -ra - py39-test-instrumentation-aws-lambda: + py310-test-instrumentation-aiopg-wrapt2: + name: instrumentation-aiopg-wrapt2 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aiopg-wrapt2 -- -ra + + py310-test-instrumentation-aws-lambda: name: instrumentation-aws-lambda runs-on: ubuntu-latest timeout-minutes: 30 @@ -581,20 +765,80 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aws-lambda -- -ra + + py310-test-instrumentation-botocore-0-wrapt1: + name: instrumentation-botocore-0-wrapt1 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-0-wrapt1 -- -ra + + py310-test-instrumentation-botocore-0-wrapt2: + name: instrumentation-botocore-0-wrapt2 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aws-lambda -- -ra + run: tox -e py310-test-instrumentation-botocore-0-wrapt2 -- -ra - py39-test-instrumentation-botocore-0: - name: instrumentation-botocore-0 + py310-test-instrumentation-botocore-1-wrapt1: + name: instrumentation-botocore-1-wrapt1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -611,20 +855,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-botocore-0 -- -ra + run: tox -e py310-test-instrumentation-botocore-1-wrapt1 -- -ra - py39-test-instrumentation-botocore-1: - name: instrumentation-botocore-1 + py310-test-instrumentation-botocore-1-wrapt2: + name: instrumentation-botocore-1-wrapt2 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -641,19 +885,79 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-botocore-1 -- -ra + run: tox -e py310-test-instrumentation-botocore-1-wrapt2 -- -ra + + py310-test-instrumentation-botocore-2: + name: instrumentation-botocore-2 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" - py39-test-instrumentation-boto3sqs: + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-2 -- -ra + + py310-test-instrumentation-botocore-3: + name: instrumentation-botocore-3 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-3 -- -ra + + py310-test-instrumentation-boto3sqs: name: instrumentation-boto3sqs runs-on: ubuntu-latest timeout-minutes: 30 @@ -671,19 +975,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-boto3sqs -- -ra + run: tox -e py310-test-instrumentation-boto3sqs -- -ra - py39-test-instrumentation-django-0: + py310-test-instrumentation-django-0: name: instrumentation-django-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -701,19 +1005,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-django-0 -- -ra + run: tox -e py310-test-instrumentation-django-0 -- -ra - py39-test-instrumentation-django-1: + py310-test-instrumentation-django-1: name: instrumentation-django-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -731,19 +1035,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-django-1 -- -ra + run: tox -e py310-test-instrumentation-django-1 -- -ra - py39-test-instrumentation-django-2: + py310-test-instrumentation-django-2: name: instrumentation-django-2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -761,20 +1065,50 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-django-2 -- -ra + + py310-test-instrumentation-django-3: + name: instrumentation-django-3 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-django-2 -- -ra + run: tox -e py310-test-instrumentation-django-3 -- -ra - py39-test-instrumentation-dbapi: - name: instrumentation-dbapi + py310-test-instrumentation-dbapi-wrapt1: + name: instrumentation-dbapi-wrapt1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -791,20 +1125,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-dbapi -- -ra + run: tox -e py310-test-instrumentation-dbapi-wrapt1 -- -ra - py39-test-instrumentation-boto: - name: instrumentation-boto + py310-test-instrumentation-dbapi-wrapt2: + name: instrumentation-dbapi-wrapt2 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -821,19 +1155,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-boto -- -ra + run: tox -e py310-test-instrumentation-dbapi-wrapt2 -- -ra - py39-test-instrumentation-asyncclick: + py310-test-instrumentation-asyncclick: name: instrumentation-asyncclick runs-on: ubuntu-latest timeout-minutes: 30 @@ -851,19 +1185,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-asyncclick -- -ra + run: tox -e py310-test-instrumentation-asyncclick -- -ra - py39-test-instrumentation-click: + py310-test-instrumentation-click: name: instrumentation-click runs-on: ubuntu-latest timeout-minutes: 30 @@ -881,19 +1215,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-click -- -ra + run: tox -e py310-test-instrumentation-click -- -ra - py39-test-instrumentation-elasticsearch-0: + py310-test-instrumentation-elasticsearch-0: name: instrumentation-elasticsearch-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -911,19 +1245,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-elasticsearch-0 -- -ra + run: tox -e py310-test-instrumentation-elasticsearch-0 -- -ra - py39-test-instrumentation-elasticsearch-1: + py310-test-instrumentation-elasticsearch-1: name: instrumentation-elasticsearch-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -941,19 +1275,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-elasticsearch-1 -- -ra + run: tox -e py310-test-instrumentation-elasticsearch-1 -- -ra - py39-test-instrumentation-elasticsearch-2: + py310-test-instrumentation-elasticsearch-2: name: instrumentation-elasticsearch-2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -971,20 +1305,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-elasticsearch-2 -- -ra + run: tox -e py310-test-instrumentation-elasticsearch-2 -- -ra - py39-test-instrumentation-falcon-0: - name: instrumentation-falcon-0 + py310-test-instrumentation-falcon-1: + name: instrumentation-falcon-1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -1001,20 +1335,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-falcon-0 -- -ra + run: tox -e py310-test-instrumentation-falcon-1 -- -ra - py39-test-instrumentation-falcon-1: - name: instrumentation-falcon-1 + py310-test-instrumentation-falcon-2: + name: instrumentation-falcon-2 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -1031,20 +1365,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-falcon-1 -- -ra + run: tox -e py310-test-instrumentation-falcon-2 -- -ra - py39-test-instrumentation-falcon-2: - name: instrumentation-falcon-2 + py310-test-instrumentation-falcon-3: + name: instrumentation-falcon-3 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -1061,20 +1395,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-falcon-2 -- -ra + run: tox -e py310-test-instrumentation-falcon-3 -- -ra - py39-test-instrumentation-falcon-3: - name: instrumentation-falcon-3 + py310-test-instrumentation-falcon-4: + name: instrumentation-falcon-4 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -1091,19 +1425,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-falcon-3 -- -ra + run: tox -e py310-test-instrumentation-falcon-4 -- -ra - py39-test-instrumentation-fastapi: + py310-test-instrumentation-fastapi: name: instrumentation-fastapi runs-on: ubuntu-latest timeout-minutes: 30 @@ -1121,19 +1455,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-fastapi -- -ra + run: tox -e py310-test-instrumentation-fastapi -- -ra - py39-test-instrumentation-flask-0: + py310-test-instrumentation-flask-0: name: instrumentation-flask-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1151,19 +1485,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-flask-0 -- -ra + run: tox -e py310-test-instrumentation-flask-0 -- -ra - py39-test-instrumentation-flask-1: + py310-test-instrumentation-flask-1: name: instrumentation-flask-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1181,19 +1515,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-flask-1 -- -ra + run: tox -e py310-test-instrumentation-flask-1 -- -ra - py39-test-instrumentation-flask-2: + py310-test-instrumentation-flask-2: name: instrumentation-flask-2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1211,19 +1545,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-flask-2 -- -ra + run: tox -e py310-test-instrumentation-flask-2 -- -ra - py39-test-instrumentation-flask-3: + py310-test-instrumentation-flask-3: name: instrumentation-flask-3 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1241,19 +1575,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-flask-3 -- -ra + run: tox -e py310-test-instrumentation-flask-3 -- -ra - py39-test-instrumentation-urllib: + py310-test-instrumentation-urllib: name: instrumentation-urllib runs-on: ubuntu-latest timeout-minutes: 30 @@ -1271,19 +1605,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-urllib -- -ra + run: tox -e py310-test-instrumentation-urllib -- -ra - py39-test-instrumentation-urllib3-0: + py310-test-instrumentation-urllib3-0: name: instrumentation-urllib3-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1301,19 +1635,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-urllib3-0 -- -ra + run: tox -e py310-test-instrumentation-urllib3-0 -- -ra - py39-test-instrumentation-urllib3-1: + py310-test-instrumentation-urllib3-1: name: instrumentation-urllib3-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1331,19 +1665,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-urllib3-1 -- -ra + run: tox -e py310-test-instrumentation-urllib3-1 -- -ra - py39-test-instrumentation-requests: + py310-test-instrumentation-requests: name: instrumentation-requests runs-on: ubuntu-latest timeout-minutes: 30 @@ -1361,19 +1695,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-requests -- -ra + run: tox -e py310-test-instrumentation-requests -- -ra - py39-test-instrumentation-starlette-oldest: + py310-test-instrumentation-starlette-oldest: name: instrumentation-starlette-oldest runs-on: ubuntu-latest timeout-minutes: 30 @@ -1391,19 +1725,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-starlette-oldest -- -ra + run: tox -e py310-test-instrumentation-starlette-oldest -- -ra - py39-test-instrumentation-starlette-latest: + py310-test-instrumentation-starlette-latest: name: instrumentation-starlette-latest runs-on: ubuntu-latest timeout-minutes: 30 @@ -1421,19 +1755,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-starlette-latest -- -ra + run: tox -e py310-test-instrumentation-starlette-latest -- -ra - py39-test-instrumentation-jinja2: + py310-test-instrumentation-jinja2: name: instrumentation-jinja2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1451,19 +1785,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-jinja2 -- -ra + run: tox -e py310-test-instrumentation-jinja2 -- -ra - py39-test-instrumentation-logging: + py310-test-instrumentation-logging: name: instrumentation-logging runs-on: ubuntu-latest timeout-minutes: 30 @@ -1481,19 +1815,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-logging -- -ra + run: tox -e py310-test-instrumentation-logging -- -ra - py39-test-exporter-richconsole: + py310-test-exporter-richconsole: name: exporter-richconsole runs-on: ubuntu-latest timeout-minutes: 30 @@ -1511,19 +1845,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-exporter-richconsole -- -ra + run: tox -e py310-test-exporter-richconsole -- -ra - py39-test-exporter-prometheus-remote-write: + py310-test-exporter-prometheus-remote-write: name: exporter-prometheus-remote-write runs-on: ubuntu-latest timeout-minutes: 30 @@ -1541,19 +1875,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-exporter-prometheus-remote-write -- -ra + run: tox -e py310-test-exporter-prometheus-remote-write -- -ra - py39-test-instrumentation-mysql-0: + py310-test-instrumentation-mysql-0: name: instrumentation-mysql-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1571,19 +1905,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-mysql-0 -- -ra + run: tox -e py310-test-instrumentation-mysql-0 -- -ra - py39-test-instrumentation-mysql-1: + py310-test-instrumentation-mysql-1: name: instrumentation-mysql-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1601,19 +1935,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-mysql-1 -- -ra + run: tox -e py310-test-instrumentation-mysql-1 -- -ra - py39-test-instrumentation-mysqlclient: + py310-test-instrumentation-mysqlclient: name: instrumentation-mysqlclient runs-on: ubuntu-latest timeout-minutes: 30 @@ -1631,19 +1965,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-mysqlclient -- -ra + run: tox -e py310-test-instrumentation-mysqlclient -- -ra - py39-test-instrumentation-psycopg2: + py310-test-instrumentation-psycopg2: name: instrumentation-psycopg2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1661,19 +1995,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-psycopg2 -- -ra + run: tox -e py310-test-instrumentation-psycopg2 -- -ra - py39-test-instrumentation-psycopg2-binary: + py310-test-instrumentation-psycopg2-binary: name: instrumentation-psycopg2-binary runs-on: ubuntu-latest timeout-minutes: 30 @@ -1691,19 +2025,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-psycopg2-binary -- -ra + run: tox -e py310-test-instrumentation-psycopg2-binary -- -ra - py39-test-instrumentation-psycopg: + py310-test-instrumentation-psycopg: name: instrumentation-psycopg runs-on: ubuntu-latest timeout-minutes: 30 @@ -1721,19 +2055,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-psycopg -- -ra + run: tox -e py310-test-instrumentation-psycopg -- -ra - py39-test-instrumentation-pymemcache-0: + py310-test-instrumentation-pymemcache-0: name: instrumentation-pymemcache-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1751,19 +2085,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-0 -- -ra + run: tox -e py310-test-instrumentation-pymemcache-0 -- -ra - py39-test-instrumentation-pymemcache-1: + py310-test-instrumentation-pymemcache-1: name: instrumentation-pymemcache-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1781,19 +2115,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-1 -- -ra + run: tox -e py310-test-instrumentation-pymemcache-1 -- -ra - py39-test-instrumentation-pymemcache-2: + py310-test-instrumentation-pymemcache-2: name: instrumentation-pymemcache-2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1811,19 +2145,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-2 -- -ra + run: tox -e py310-test-instrumentation-pymemcache-2 -- -ra - py39-test-instrumentation-pymemcache-3: + py310-test-instrumentation-pymemcache-3: name: instrumentation-pymemcache-3 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1841,19 +2175,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-3 -- -ra + run: tox -e py310-test-instrumentation-pymemcache-3 -- -ra - py39-test-instrumentation-pymemcache-4: + py310-test-instrumentation-pymemcache-4: name: instrumentation-pymemcache-4 runs-on: ubuntu-latest timeout-minutes: 30 @@ -1871,19 +2205,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-4 -- -ra + run: tox -e py310-test-instrumentation-pymemcache-4 -- -ra - py39-test-instrumentation-pymongo: + py310-test-instrumentation-pymongo: name: instrumentation-pymongo runs-on: ubuntu-latest timeout-minutes: 30 @@ -1901,19 +2235,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymongo -- -ra + run: tox -e py310-test-instrumentation-pymongo -- -ra - py39-test-instrumentation-pymysql: + py310-test-instrumentation-pymysql: name: instrumentation-pymysql runs-on: ubuntu-latest timeout-minutes: 30 @@ -1931,19 +2265,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymysql -- -ra + run: tox -e py310-test-instrumentation-pymysql -- -ra - py39-test-instrumentation-pymssql: + py310-test-instrumentation-pymssql: name: instrumentation-pymssql runs-on: ubuntu-latest timeout-minutes: 30 @@ -1961,19 +2295,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pymssql -- -ra + run: tox -e py310-test-instrumentation-pymssql -- -ra - py39-test-instrumentation-pyramid: + py310-test-instrumentation-pyramid: name: instrumentation-pyramid runs-on: ubuntu-latest timeout-minutes: 30 @@ -1991,19 +2325,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-pyramid -- -ra + run: tox -e py310-test-instrumentation-pyramid -- -ra - py39-test-instrumentation-asgi: + py310-test-instrumentation-asgi: name: instrumentation-asgi runs-on: ubuntu-latest timeout-minutes: 30 @@ -2021,20 +2355,50 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-asgi -- -ra + + py310-test-instrumentation-asyncpg-wrapt1: + name: instrumentation-asyncpg-wrapt1 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-asgi -- -ra + run: tox -e py310-test-instrumentation-asyncpg-wrapt1 -- -ra - py39-test-instrumentation-asyncpg: - name: instrumentation-asyncpg + py310-test-instrumentation-asyncpg-wrapt2: + name: instrumentation-asyncpg-wrapt2 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -2051,19 +2415,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-asyncpg -- -ra + run: tox -e py310-test-instrumentation-asyncpg-wrapt2 -- -ra - py39-test-instrumentation-sqlite3: + py310-test-instrumentation-sqlite3: name: instrumentation-sqlite3 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2081,19 +2445,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-sqlite3 -- -ra + run: tox -e py310-test-instrumentation-sqlite3 -- -ra - py39-test-instrumentation-wsgi: + py310-test-instrumentation-wsgi: name: instrumentation-wsgi runs-on: ubuntu-latest timeout-minutes: 30 @@ -2111,20 +2475,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-wsgi -- -ra + run: tox -e py310-test-instrumentation-wsgi -- -ra - py39-test-instrumentation-grpc-0: - name: instrumentation-grpc-0 + py310-test-instrumentation-grpc-0-wrapt1: + name: instrumentation-grpc-0-wrapt1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -2141,20 +2505,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-grpc-0 -- -ra + run: tox -e py310-test-instrumentation-grpc-0-wrapt1 -- -ra - py39-test-instrumentation-grpc-1: - name: instrumentation-grpc-1 + py310-test-instrumentation-grpc-0-wrapt2: + name: instrumentation-grpc-0-wrapt2 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -2171,19 +2535,79 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-grpc-1 -- -ra + run: tox -e py310-test-instrumentation-grpc-0-wrapt2 -- -ra + + py310-test-instrumentation-grpc-1-wrapt1: + name: instrumentation-grpc-1-wrapt1 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} - py39-test-instrumentation-sqlalchemy-1: + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-grpc-1-wrapt1 -- -ra + + py310-test-instrumentation-grpc-1-wrapt2: + name: instrumentation-grpc-1-wrapt2 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-grpc-1-wrapt2 -- -ra + + py310-test-instrumentation-sqlalchemy-1: name: instrumentation-sqlalchemy-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2201,19 +2625,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-sqlalchemy-1 -- -ra + run: tox -e py310-test-instrumentation-sqlalchemy-1 -- -ra - py39-test-instrumentation-sqlalchemy-2: + py310-test-instrumentation-sqlalchemy-2: name: instrumentation-sqlalchemy-2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2231,19 +2655,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-sqlalchemy-2 -- -ra + run: tox -e py310-test-instrumentation-sqlalchemy-2 -- -ra - py39-test-instrumentation-redis: + py310-test-instrumentation-redis: name: instrumentation-redis runs-on: ubuntu-latest timeout-minutes: 30 @@ -2261,19 +2685,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-redis -- -ra + run: tox -e py310-test-instrumentation-redis -- -ra - py39-test-instrumentation-remoulade: + py310-test-instrumentation-remoulade: name: instrumentation-remoulade runs-on: ubuntu-latest timeout-minutes: 30 @@ -2291,19 +2715,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-remoulade -- -ra + run: tox -e py310-test-instrumentation-remoulade -- -ra - py39-test-instrumentation-celery: + py310-test-instrumentation-celery: name: instrumentation-celery runs-on: ubuntu-latest timeout-minutes: 30 @@ -2321,19 +2745,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-celery -- -ra + run: tox -e py310-test-instrumentation-celery -- -ra - py39-test-instrumentation-system-metrics: + py310-test-instrumentation-system-metrics: name: instrumentation-system-metrics runs-on: ubuntu-latest timeout-minutes: 30 @@ -2351,19 +2775,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-system-metrics -- -ra + run: tox -e py310-test-instrumentation-system-metrics -- -ra - py39-test-instrumentation-threading: + py310-test-instrumentation-threading: name: instrumentation-threading runs-on: ubuntu-latest timeout-minutes: 30 @@ -2381,19 +2805,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-threading -- -ra + run: tox -e py310-test-instrumentation-threading -- -ra - py39-test-instrumentation-tornado: + py310-test-instrumentation-tornado: name: instrumentation-tornado runs-on: ubuntu-latest timeout-minutes: 30 @@ -2411,19 +2835,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-tornado -- -ra + run: tox -e py310-test-instrumentation-tornado -- -ra - py39-test-instrumentation-tortoiseorm: + py310-test-instrumentation-tortoiseorm: name: instrumentation-tortoiseorm runs-on: ubuntu-latest timeout-minutes: 30 @@ -2441,20 +2865,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-tortoiseorm -- -ra + run: tox -e py310-test-instrumentation-tortoiseorm -- -ra - py39-test-instrumentation-httpx-0: - name: instrumentation-httpx-0 + py310-test-instrumentation-httpx-0-wrapt1: + name: instrumentation-httpx-0-wrapt1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -2471,20 +2895,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-httpx-0 -- -ra + run: tox -e py310-test-instrumentation-httpx-0-wrapt1 -- -ra - py39-test-instrumentation-httpx-1: - name: instrumentation-httpx-1 + py310-test-instrumentation-httpx-0-wrapt2: + name: instrumentation-httpx-0-wrapt2 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -2501,19 +2925,79 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-httpx-1 -- -ra + run: tox -e py310-test-instrumentation-httpx-0-wrapt2 -- -ra + + py310-test-instrumentation-httpx-1-wrapt1: + name: instrumentation-httpx-1-wrapt1 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} - py39-test-util-http: + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-httpx-1-wrapt1 -- -ra + + py310-test-instrumentation-httpx-1-wrapt2: + name: instrumentation-httpx-1-wrapt2 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-httpx-1-wrapt2 -- -ra + + py310-test-util-http: name: util-http runs-on: ubuntu-latest timeout-minutes: 30 @@ -2531,19 +3015,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-util-http -- -ra + run: tox -e py310-test-util-http -- -ra - py39-test-exporter-credential-provider-gcp: + py310-test-exporter-credential-provider-gcp: name: exporter-credential-provider-gcp runs-on: ubuntu-latest timeout-minutes: 30 @@ -2561,19 +3045,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-exporter-credential-provider-gcp -- -ra + run: tox -e py310-test-exporter-credential-provider-gcp -- -ra - py39-test-util-genai: + py310-test-util-genai: name: util-genai runs-on: ubuntu-latest timeout-minutes: 30 @@ -2591,19 +3075,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-util-genai -- -ra + run: tox -e py310-test-util-genai -- -ra - py39-test-propagator-aws-xray-0: + py310-test-propagator-aws-xray-0: name: propagator-aws-xray-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2621,19 +3105,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-propagator-aws-xray-0 -- -ra + run: tox -e py310-test-propagator-aws-xray-0 -- -ra - py39-test-propagator-aws-xray-1: + py310-test-propagator-aws-xray-1: name: propagator-aws-xray-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2651,19 +3135,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-propagator-aws-xray-1 -- -ra + run: tox -e py310-test-propagator-aws-xray-1 -- -ra - py39-test-propagator-ot-trace: + py310-test-propagator-ot-trace: name: propagator-ot-trace runs-on: ubuntu-latest timeout-minutes: 30 @@ -2681,20 +3165,80 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-propagator-ot-trace -- -ra + + py310-test-instrumentation-sio-pika-0-wrapt1: + name: instrumentation-sio-pika-0-wrapt1 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sio-pika-0-wrapt1 -- -ra + + py310-test-instrumentation-sio-pika-0-wrapt2: + name: instrumentation-sio-pika-0-wrapt2 + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-propagator-ot-trace -- -ra + run: tox -e py310-test-instrumentation-sio-pika-0-wrapt2 -- -ra - py39-test-instrumentation-sio-pika-0: - name: instrumentation-sio-pika-0 + py310-test-instrumentation-sio-pika-1-wrapt1: + name: instrumentation-sio-pika-1-wrapt1 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -2711,20 +3255,20 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-sio-pika-0 -- -ra + run: tox -e py310-test-instrumentation-sio-pika-1-wrapt1 -- -ra - py39-test-instrumentation-sio-pika-1: - name: instrumentation-sio-pika-1 + py310-test-instrumentation-sio-pika-1-wrapt2: + name: instrumentation-sio-pika-1-wrapt2 runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -2741,19 +3285,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-sio-pika-1 -- -ra + run: tox -e py310-test-instrumentation-sio-pika-1-wrapt2 -- -ra - py39-test-instrumentation-aio-pika-0: + py310-test-instrumentation-aio-pika-0: name: instrumentation-aio-pika-0 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2771,19 +3315,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-0 -- -ra + run: tox -e py310-test-instrumentation-aio-pika-0 -- -ra - py39-test-instrumentation-aio-pika-1: + py310-test-instrumentation-aio-pika-1: name: instrumentation-aio-pika-1 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2801,19 +3345,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-1 -- -ra + run: tox -e py310-test-instrumentation-aio-pika-1 -- -ra - py39-test-instrumentation-aio-pika-2: + py310-test-instrumentation-aio-pika-2: name: instrumentation-aio-pika-2 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2831,19 +3375,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-2 -- -ra + run: tox -e py310-test-instrumentation-aio-pika-2 -- -ra - py39-test-instrumentation-aio-pika-3: + py310-test-instrumentation-aio-pika-3: name: instrumentation-aio-pika-3 runs-on: ubuntu-latest timeout-minutes: 30 @@ -2861,19 +3405,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-3 -- -ra + run: tox -e py310-test-instrumentation-aio-pika-3 -- -ra - py39-test-instrumentation-aiokafka: + py310-test-instrumentation-aiokafka: name: instrumentation-aiokafka runs-on: ubuntu-latest timeout-minutes: 30 @@ -2891,19 +3435,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-aiokafka -- -ra + run: tox -e py310-test-instrumentation-aiokafka -- -ra - py39-test-instrumentation-kafka-python: + py310-test-instrumentation-kafka-python: name: instrumentation-kafka-python runs-on: ubuntu-latest timeout-minutes: 30 @@ -2921,19 +3465,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-kafka-python -- -ra + run: tox -e py310-test-instrumentation-kafka-python -- -ra - py39-test-instrumentation-kafka-pythonng: + py310-test-instrumentation-kafka-pythonng: name: instrumentation-kafka-pythonng runs-on: ubuntu-latest timeout-minutes: 30 @@ -2951,19 +3495,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-kafka-pythonng -- -ra + run: tox -e py310-test-instrumentation-kafka-pythonng -- -ra - py39-test-instrumentation-confluent-kafka: + py310-test-instrumentation-confluent-kafka: name: instrumentation-confluent-kafka runs-on: ubuntu-latest timeout-minutes: 30 @@ -2981,19 +3525,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-confluent-kafka -- -ra + run: tox -e py310-test-instrumentation-confluent-kafka -- -ra - py39-test-instrumentation-asyncio: + py310-test-instrumentation-asyncio: name: instrumentation-asyncio runs-on: ubuntu-latest timeout-minutes: 30 @@ -3011,19 +3555,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-asyncio -- -ra + run: tox -e py310-test-instrumentation-asyncio -- -ra - py39-test-instrumentation-cassandra-driver: + py310-test-instrumentation-cassandra-driver: name: instrumentation-cassandra-driver runs-on: ubuntu-latest timeout-minutes: 30 @@ -3041,19 +3585,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-cassandra-driver -- -ra + run: tox -e py310-test-instrumentation-cassandra-driver -- -ra - py39-test-instrumentation-cassandra-scylla: + py310-test-instrumentation-cassandra-scylla: name: instrumentation-cassandra-scylla runs-on: ubuntu-latest timeout-minutes: 30 @@ -3071,19 +3615,19 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-instrumentation-cassandra-scylla -- -ra + run: tox -e py310-test-instrumentation-cassandra-scylla -- -ra - py39-test-processor-baggage: + py310-test-processor-baggage: name: processor-baggage runs-on: ubuntu-latest timeout-minutes: 30 @@ -3101,14 +3645,74 @@ jobs: ref: ${{ env.CORE_REPO_SHA }} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-processor-baggage -- -ra + + py310-test-opamp-client-latest: + name: opamp-client-latest + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + architecture: "x64" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-opamp-client-latest -- -ra + + py310-test-opamp-client-lowest: + name: opamp-client-lowest + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout contrib repo @ SHA - ${{ env.CONTRIB_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python-contrib + ref: ${{ env.CONTRIB_REPO_SHA }} + + - name: Checkout core repo @ SHA - ${{ env.CORE_REPO_SHA }} + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-python + ref: ${{ env.CORE_REPO_SHA }} + path: opentelemetry-python + + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox run: pip install tox-uv - name: Run tests - run: tox -e py39-test-processor-baggage -- -ra + run: tox -e py310-test-opamp-client-lowest -- -ra diff --git a/.github/workflows/generate_workflows.py b/.github/workflows/generate_workflows.py index 09391a240c..7aacf708f0 100644 --- a/.github/workflows/generate_workflows.py +++ b/.github/workflows/generate_workflows.py @@ -1,6 +1,7 @@ from pathlib import Path from generate_workflows_lib import ( + generate_ci_workflow, generate_contrib_workflow, generate_lint_workflow, generate_misc_workflow, @@ -14,3 +15,4 @@ generate_lint_workflow(tox_ini_path, workflows_directory_path) generate_misc_workflow(tox_ini_path, workflows_directory_path) generate_contrib_workflow(workflows_directory_path) +generate_ci_workflow(workflows_directory_path) diff --git a/.github/workflows/generate_workflows_lib/pyproject.toml b/.github/workflows/generate_workflows_lib/pyproject.toml index 04106efc0f..16bba5576c 100644 --- a/.github/workflows/generate_workflows_lib/pyproject.toml +++ b/.github/workflows/generate_workflows_lib/pyproject.toml @@ -7,7 +7,7 @@ name = "generate-workflows-lib" dynamic = ["version"] description = "A library to generate workflows" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -17,7 +17,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py index 30f306ea5c..10f4ad921f 100644 --- a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py +++ b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py @@ -14,7 +14,7 @@ ) _tox_lint_env_regex = re_compile(r"lint-(?P[-\w]+)") _tox_contrib_env_regex = re_compile( - r"py39-test-(?P[-\w]+\w)-?(?P\d+)?" + r"py310-test-(?P[-\w]+\w)-?(?P\d+)?" ) @@ -47,9 +47,7 @@ def get_test_job_datas(tox_envs: list, operating_systems: list) -> list: os_alias = {"ubuntu-latest": "Ubuntu", "windows-latest": "Windows"} python_version_alias = { - "pypy3": "pypy-3.9", - "pypy310": "pypy-3.10", - "py39": "3.9", + "pypy3": "pypy-3.10", "py310": "3.10", "py311": "3.11", "py312": "3.12", @@ -172,25 +170,18 @@ def get_misc_job_datas(tox_envs: list) -> list: def _generate_workflow( - job_datas: list, name: str, workflow_directory_path: Path, max_jobs=250 -): - # Github seems to limit the amount of jobs in a workflow file, that is why - # they are split in groups of 250 per workflow file. - for file_number, job_datas in enumerate( - [ - job_datas[index : index + max_jobs] - for index in range(0, len(job_datas), max_jobs) - ] - ): - with open( - workflow_directory_path.joinpath(f"{name}_{file_number}.yml"), "w" - ) as test_yml_file: - test_yml_file.write( - Environment(loader=FileSystemLoader(Path(__file__).parent)) - .get_template(f"{name}.yml.j2") - .render(job_datas=job_datas, file_number=file_number) + job_datas: list, name: str, workflow_directory_path: Path +) -> None: + env = Environment(loader=FileSystemLoader(Path(__file__).parent)) + with open( + workflow_directory_path.joinpath(f"{name}.yml"), "w" + ) as yml_file: + yml_file.write( + env.get_template(f"{name}.yml.j2").render( + job_datas=job_datas, ) - test_yml_file.write("\n") + ) + yml_file.write("\n") def generate_test_workflow( @@ -235,3 +226,15 @@ def generate_misc_workflow( "misc", workflow_directory_path, ) + + +def generate_ci_workflow( + workflow_directory_path: Path, +) -> None: + with open(workflow_directory_path.joinpath("ci.yml"), "w") as ci_yml_file: + ci_yml_file.write( + Environment(loader=FileSystemLoader(Path(__file__).parent)) + .get_template("ci.yml.j2") + .render() + ) + ci_yml_file.write("\n") diff --git a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/ci.yml.j2 b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/ci.yml.j2 new file mode 100644 index 0000000000..746adf13b6 --- /dev/null +++ b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/ci.yml.j2 @@ -0,0 +1,38 @@ +# Do not edit this file. +# This file is generated automatically by executing tox -e generate-workflows + +name: CI + +on: + push: + branches: + - 'main' + pull_request: + +permissions: + contents: read + +concurrency: + group: ${% raw %}{{ github.workflow }}-${{ github.head_ref || github.run_id }}{% endraw %} + cancel-in-progress: true + +jobs: + misc: + uses: ./.github/workflows/misc.yml + lint: + uses: ./.github/workflows/lint.yml + tests: + uses: ./.github/workflows/test.yml + + check: + if: always() + needs: + - misc + - lint + - tests + runs-on: ubuntu-latest + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${% raw %}{{ toJSON(needs) }}{% endraw %} diff --git a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/core_contrib_test.yml.j2 b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/core_contrib_test.yml.j2 index c8d2af59d0..46090ed333 100644 --- a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/core_contrib_test.yml.j2 +++ b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/core_contrib_test.yml.j2 @@ -20,6 +20,10 @@ env: CORE_REPO_SHA: ${% raw %}{{ inputs.CORE_REPO_SHA }}{% endraw %} CONTRIB_REPO_SHA: ${% raw %}{{ inputs.CONTRIB_REPO_SHA }}{% endraw %} PIP_EXISTS_ACTION: w + CORE_REPO_API: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python/opentelemetry-api + CORE_REPO_SDK: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python/opentelemetry-sdk + CORE_REPO_SEMCONV: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python/opentelemetry-semantic-conventions + CORE_REPO_TEST_UTILS: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python/tests/opentelemetry-test-utils jobs: {%- for job_data in job_datas %} @@ -42,10 +46,10 @@ jobs: ref: ${% raw %}{{ env.CORE_REPO_SHA }}{% endraw %} path: opentelemetry-python - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" architecture: "x64" - name: Install tox diff --git a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/lint.yml.j2 b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/lint.yml.j2 index 87fc1a0a0f..db05da7a6b 100644 --- a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/lint.yml.j2 +++ b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/lint.yml.j2 @@ -1,22 +1,14 @@ # Do not edit this file. # This file is generated automatically by executing tox -e generate-workflows -name: Lint {{ file_number }} +name: Lint on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: + workflow_call: permissions: contents: read -concurrency: - group: ${% raw %}{{ github.workflow }}-${{ github.head_ref || github.run_id }}{% endraw %} - cancel-in-progress: true - env: # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' # For PRs you can change the inner fallback ('main') diff --git a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/misc.yml.j2 b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/misc.yml.j2 index 4bc5f38e87..c356b8396d 100644 --- a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/misc.yml.j2 +++ b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/misc.yml.j2 @@ -1,22 +1,14 @@ # Do not edit this file. # This file is generated automatically by executing tox -e generate-workflows -name: Misc {{ file_number }} +name: Misc on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: + workflow_call: permissions: contents: read -concurrency: - group: ${% raw %}{{ github.workflow }}-${{ github.head_ref || github.run_id }}{% endraw %} - cancel-in-progress: true - env: # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' # For PRs you can change the inner fallback ('main') diff --git a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/test.yml.j2 b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/test.yml.j2 index b8f45b840f..5002f85f4d 100644 --- a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/test.yml.j2 +++ b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/test.yml.j2 @@ -1,22 +1,14 @@ # Do not edit this file. # This file is generated automatically by executing tox -e generate-workflows -name: Test {{ file_number }} +name: Test on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: + workflow_call: permissions: contents: read -concurrency: - group: ${% raw %}{{ github.workflow }}-${{ github.head_ref || github.run_id }}{% endraw %} - cancel-in-progress: true - env: # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' # For PRs you can change the inner fallback ('main') diff --git a/.github/workflows/lint_0.yml b/.github/workflows/lint.yml similarity index 99% rename from .github/workflows/lint_0.yml rename to .github/workflows/lint.yml index cb7b726df9..6bf3c631cd 100644 --- a/.github/workflows/lint_0.yml +++ b/.github/workflows/lint.yml @@ -1,22 +1,14 @@ # Do not edit this file. # This file is generated automatically by executing tox -e generate-workflows -name: Lint 0 +name: Lint on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: + workflow_call: permissions: contents: read -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - env: # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' # For PRs you can change the inner fallback ('main') @@ -393,25 +385,6 @@ jobs: - name: Run tests run: tox -e lint-instrumentation-dbapi - lint-instrumentation-boto: - name: instrumentation-boto - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e lint-instrumentation-boto - lint-instrumentation-asyncclick: name: instrumentation-asyncclick runs-on: ubuntu-latest @@ -1361,3 +1334,22 @@ jobs: - name: Run tests run: tox -e lint-processor-baggage + + lint-opamp-client: + name: opamp-client + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e lint-opamp-client diff --git a/.github/workflows/misc_0.yml b/.github/workflows/misc.yml similarity index 95% rename from .github/workflows/misc_0.yml rename to .github/workflows/misc.yml index 18a1a499a3..652d90942a 100644 --- a/.github/workflows/misc_0.yml +++ b/.github/workflows/misc.yml @@ -1,22 +1,14 @@ # Do not edit this file. # This file is generated automatically by executing tox -e generate-workflows -name: Misc 0 +name: Misc on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: + workflow_call: permissions: contents: read -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - env: # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' # For PRs you can change the inner fallback ('main') diff --git a/.github/workflows/package-prepare-patch-release.yml b/.github/workflows/package-prepare-patch-release.yml index b7061e03e2..5ad5615978 100644 --- a/.github/workflows/package-prepare-patch-release.yml +++ b/.github/workflows/package-prepare-patch-release.yml @@ -5,6 +5,7 @@ on: package: type: choice options: + - opentelemetry-opamp-client - opentelemetry-propagator-aws-xray - opentelemetry-resource-detector-azure - opentelemetry-sdk-extension-aws @@ -96,10 +97,10 @@ jobs: # replace the version in the version file (1.2.3 -> 1.2.4) sed -i -E "s/__version__\s*=\s*\"${VERSION}\"/__version__ = \"${NEXT_VERSION}\"/g" $VERSION_FILE - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install tox run: pip install tox - name: run tox diff --git a/.github/workflows/package-prepare-release.yml b/.github/workflows/package-prepare-release.yml index f0bee0a606..f9819d3668 100644 --- a/.github/workflows/package-prepare-release.yml +++ b/.github/workflows/package-prepare-release.yml @@ -5,6 +5,7 @@ on: package: type: choice options: + - opentelemetry-opamp-client - opentelemetry-propagator-aws-xray - opentelemetry-resource-detector-azure - opentelemetry-sdk-extension-aws @@ -124,10 +125,10 @@ jobs: # replace the version in the version file (1.2.3dev -> 1.2.3) sed -i -E "s/__version__\s*=\s*\"${VERSION}\.dev\"/__version__ = \"${VERSION}\"/g" $VERSION_FILE - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install tox run: pip install tox - name: run tox @@ -184,10 +185,10 @@ jobs: # replace the version in the version file (1.2.3dev -> 1.3.3.dev) sed -i -E "s/__version__\s*=\s*\"${VERSION}\.dev\"/__version__ = \"${NEXT_VERSION}.dev\"/g" $VERSION_FILE - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install tox run: pip install tox - name: run tox diff --git a/.github/workflows/package-release.yml b/.github/workflows/package-release.yml index 1340bf967c..3ab6341b55 100644 --- a/.github/workflows/package-release.yml +++ b/.github/workflows/package-release.yml @@ -5,6 +5,7 @@ on: package: type: choice options: + - opentelemetry-opamp-client - opentelemetry-propagator-aws-xray - opentelemetry-resource-detector-azure - opentelemetry-sdk-extension-aws @@ -87,7 +88,7 @@ jobs: # next few steps publish to pypi - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' - name: Build wheels run: ./scripts/build_a_package.sh diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 577fc86ba4..b2ebdf41f1 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -59,10 +59,10 @@ jobs: - name: Update version run: .github/scripts/update-version-patch.sh $STABLE_VERSION $UNSTABLE_VERSION $STABLE_VERSION_PREV $UNSTABLE_VERSION_PREV - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install tox run: pip install tox - name: run tox diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml index 1017a49a70..6804d112a6 100644 --- a/.github/workflows/prepare-release-branch.yml +++ b/.github/workflows/prepare-release-branch.yml @@ -82,10 +82,10 @@ jobs: - name: Update version run: .github/scripts/update-version.sh $STABLE_VERSION $UNSTABLE_VERSION - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install tox run: pip install tox - name: run tox @@ -184,10 +184,10 @@ jobs: - name: Update version run: .github/scripts/update-version.sh $STABLE_NEXT_VERSION $UNSTABLE_NEXT_VERSION - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install tox run: pip install tox - name: run tox diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8fbb0dd36..d7190f687f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: # next few steps publish to pypi - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' - name: Build wheels run: ./scripts/build.sh diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index f212b5e035..b30c6a571c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -33,3 +33,5 @@ jobs: This PR has been closed due to inactivity. Please reopen if you would like to continue working on it. exempt-pr-labels: "hold,WIP,blocked-by-spec,do not merge" + # TODO: Revert back to default of 30 after we have cleared the backlog of stale PRs. + operations-per-run: 500 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..9c694007ea --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,12717 @@ +# Do not edit this file. +# This file is generated automatically by executing tox -e generate-workflows + +name: Test + +on: + workflow_call: + +permissions: + contents: read + +env: + # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' + # For PRs you can change the inner fallback ('main') + # For pushes you change the outer fallback ('main') + # The logic below is used during releases and depends on having an equivalent branch name in the core repo. + CORE_REPO_SHA: ${{ github.event_name == 'pull_request' && ( + contains(github.event.pull_request.labels.*.name, 'prepare-release') && github.event.pull_request.head.ref || + contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref || + 'main' + ) || 'main' }} + CONTRIB_REPO_SHA: main + PIP_EXISTS_ACTION: w + +jobs: + + py310-test-instrumentation-openai-v2-oldest_ubuntu-latest: + name: instrumentation-openai-v2-oldest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-openai-v2-oldest -- -ra + + py310-test-instrumentation-openai-v2-latest_ubuntu-latest: + name: instrumentation-openai-v2-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-openai-v2-latest -- -ra + + py311-test-instrumentation-openai-v2-oldest_ubuntu-latest: + name: instrumentation-openai-v2-oldest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-openai-v2-oldest -- -ra + + py311-test-instrumentation-openai-v2-latest_ubuntu-latest: + name: instrumentation-openai-v2-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-openai-v2-latest -- -ra + + py312-test-instrumentation-openai-v2-oldest_ubuntu-latest: + name: instrumentation-openai-v2-oldest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-openai-v2-oldest -- -ra + + py312-test-instrumentation-openai-v2-latest_ubuntu-latest: + name: instrumentation-openai-v2-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-openai-v2-latest -- -ra + + py313-test-instrumentation-openai-v2-oldest_ubuntu-latest: + name: instrumentation-openai-v2-oldest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-openai-v2-oldest -- -ra + + py313-test-instrumentation-openai-v2-latest_ubuntu-latest: + name: instrumentation-openai-v2-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-openai-v2-latest -- -ra + + py314-test-instrumentation-openai-v2-oldest_ubuntu-latest: + name: instrumentation-openai-v2-oldest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-openai-v2-oldest -- -ra + + py314-test-instrumentation-openai-v2-latest_ubuntu-latest: + name: instrumentation-openai-v2-latest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-openai-v2-latest -- -ra + + py313-test-instrumentation-openai-v2-pydantic1_ubuntu-latest: + name: instrumentation-openai-v2-pydantic1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-openai-v2-pydantic1 -- -ra + + pypy3-test-instrumentation-openai-v2-oldest_ubuntu-latest: + name: instrumentation-openai-v2-oldest pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-openai-v2-oldest -- -ra + + pypy3-test-instrumentation-openai-v2-latest_ubuntu-latest: + name: instrumentation-openai-v2-latest pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-openai-v2-latest -- -ra + + py310-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: + name: instrumentation-openai_agents-v2-oldest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-openai_agents-v2-oldest -- -ra + + py310-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: + name: instrumentation-openai_agents-v2-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-openai_agents-v2-latest -- -ra + + py311-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: + name: instrumentation-openai_agents-v2-oldest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-openai_agents-v2-oldest -- -ra + + py311-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: + name: instrumentation-openai_agents-v2-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-openai_agents-v2-latest -- -ra + + py312-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: + name: instrumentation-openai_agents-v2-oldest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-openai_agents-v2-oldest -- -ra + + py312-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: + name: instrumentation-openai_agents-v2-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-openai_agents-v2-latest -- -ra + + py313-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: + name: instrumentation-openai_agents-v2-oldest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-openai_agents-v2-oldest -- -ra + + py313-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: + name: instrumentation-openai_agents-v2-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-openai_agents-v2-latest -- -ra + + py314-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: + name: instrumentation-openai_agents-v2-oldest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-openai_agents-v2-oldest -- -ra + + py314-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: + name: instrumentation-openai_agents-v2-latest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-openai_agents-v2-latest -- -ra + + py310-test-instrumentation-vertexai-oldest_ubuntu-latest: + name: instrumentation-vertexai-oldest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-vertexai-oldest -- -ra + + py310-test-instrumentation-vertexai-latest_ubuntu-latest: + name: instrumentation-vertexai-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-vertexai-latest -- -ra + + py311-test-instrumentation-vertexai-oldest_ubuntu-latest: + name: instrumentation-vertexai-oldest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-vertexai-oldest -- -ra + + py311-test-instrumentation-vertexai-latest_ubuntu-latest: + name: instrumentation-vertexai-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-vertexai-latest -- -ra + + py312-test-instrumentation-vertexai-oldest_ubuntu-latest: + name: instrumentation-vertexai-oldest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-vertexai-oldest -- -ra + + py312-test-instrumentation-vertexai-latest_ubuntu-latest: + name: instrumentation-vertexai-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-vertexai-latest -- -ra + + py313-test-instrumentation-vertexai-oldest_ubuntu-latest: + name: instrumentation-vertexai-oldest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-vertexai-oldest -- -ra + + py313-test-instrumentation-vertexai-latest_ubuntu-latest: + name: instrumentation-vertexai-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-vertexai-latest -- -ra + + py314-test-instrumentation-vertexai-oldest_ubuntu-latest: + name: instrumentation-vertexai-oldest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-vertexai-oldest -- -ra + + py314-test-instrumentation-vertexai-latest_ubuntu-latest: + name: instrumentation-vertexai-latest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-vertexai-latest -- -ra + + py310-test-instrumentation-google-genai-oldest_ubuntu-latest: + name: instrumentation-google-genai-oldest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-google-genai-oldest -- -ra + + py310-test-instrumentation-google-genai-latest_ubuntu-latest: + name: instrumentation-google-genai-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-google-genai-latest -- -ra + + py311-test-instrumentation-google-genai-oldest_ubuntu-latest: + name: instrumentation-google-genai-oldest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-google-genai-oldest -- -ra + + py311-test-instrumentation-google-genai-latest_ubuntu-latest: + name: instrumentation-google-genai-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-google-genai-latest -- -ra + + py312-test-instrumentation-google-genai-oldest_ubuntu-latest: + name: instrumentation-google-genai-oldest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-google-genai-oldest -- -ra + + py312-test-instrumentation-google-genai-latest_ubuntu-latest: + name: instrumentation-google-genai-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-google-genai-latest -- -ra + + py313-test-instrumentation-google-genai-oldest_ubuntu-latest: + name: instrumentation-google-genai-oldest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-google-genai-oldest -- -ra + + py313-test-instrumentation-google-genai-latest_ubuntu-latest: + name: instrumentation-google-genai-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-google-genai-latest -- -ra + + py314-test-instrumentation-google-genai-oldest_ubuntu-latest: + name: instrumentation-google-genai-oldest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-google-genai-oldest -- -ra + + py314-test-instrumentation-google-genai-latest_ubuntu-latest: + name: instrumentation-google-genai-latest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-google-genai-latest -- -ra + + py310-test-instrumentation-anthropic-oldest_ubuntu-latest: + name: instrumentation-anthropic-oldest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-anthropic-oldest -- -ra + + py310-test-instrumentation-anthropic-latest_ubuntu-latest: + name: instrumentation-anthropic-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-anthropic-latest -- -ra + + py311-test-instrumentation-anthropic-oldest_ubuntu-latest: + name: instrumentation-anthropic-oldest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-anthropic-oldest -- -ra + + py311-test-instrumentation-anthropic-latest_ubuntu-latest: + name: instrumentation-anthropic-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-anthropic-latest -- -ra + + py312-test-instrumentation-anthropic-oldest_ubuntu-latest: + name: instrumentation-anthropic-oldest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-anthropic-oldest -- -ra + + py312-test-instrumentation-anthropic-latest_ubuntu-latest: + name: instrumentation-anthropic-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-anthropic-latest -- -ra + + py313-test-instrumentation-anthropic-oldest_ubuntu-latest: + name: instrumentation-anthropic-oldest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-anthropic-oldest -- -ra + + py313-test-instrumentation-anthropic-latest_ubuntu-latest: + name: instrumentation-anthropic-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-anthropic-latest -- -ra + + py314-test-instrumentation-anthropic-oldest_ubuntu-latest: + name: instrumentation-anthropic-oldest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-anthropic-oldest -- -ra + + py314-test-instrumentation-anthropic-latest_ubuntu-latest: + name: instrumentation-anthropic-latest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-anthropic-latest -- -ra + + py310-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-oldest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-claude-agent-sdk-oldest -- -ra + + py310-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-claude-agent-sdk-latest -- -ra + + py311-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-oldest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-claude-agent-sdk-oldest -- -ra + + py311-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-claude-agent-sdk-latest -- -ra + + py312-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-oldest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-claude-agent-sdk-oldest -- -ra + + py312-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-claude-agent-sdk-latest -- -ra + + py313-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-oldest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-claude-agent-sdk-oldest -- -ra + + py313-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: + name: instrumentation-claude-agent-sdk-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-claude-agent-sdk-latest -- -ra + + py310-test-resource-detector-containerid_ubuntu-latest: + name: resource-detector-containerid 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-resource-detector-containerid -- -ra + + py311-test-resource-detector-containerid_ubuntu-latest: + name: resource-detector-containerid 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-resource-detector-containerid -- -ra + + py312-test-resource-detector-containerid_ubuntu-latest: + name: resource-detector-containerid 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-resource-detector-containerid -- -ra + + py313-test-resource-detector-containerid_ubuntu-latest: + name: resource-detector-containerid 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-resource-detector-containerid -- -ra + + py314-test-resource-detector-containerid_ubuntu-latest: + name: resource-detector-containerid 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-resource-detector-containerid -- -ra + + pypy3-test-resource-detector-containerid_ubuntu-latest: + name: resource-detector-containerid pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-resource-detector-containerid -- -ra + + py310-test-resource-detector-azure-0_ubuntu-latest: + name: resource-detector-azure-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-resource-detector-azure-0 -- -ra + + py310-test-resource-detector-azure-1_ubuntu-latest: + name: resource-detector-azure-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-resource-detector-azure-1 -- -ra + + py311-test-resource-detector-azure-0_ubuntu-latest: + name: resource-detector-azure-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-resource-detector-azure-0 -- -ra + + py311-test-resource-detector-azure-1_ubuntu-latest: + name: resource-detector-azure-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-resource-detector-azure-1 -- -ra + + py312-test-resource-detector-azure-0_ubuntu-latest: + name: resource-detector-azure-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-resource-detector-azure-0 -- -ra + + py312-test-resource-detector-azure-1_ubuntu-latest: + name: resource-detector-azure-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-resource-detector-azure-1 -- -ra + + py313-test-resource-detector-azure-0_ubuntu-latest: + name: resource-detector-azure-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-resource-detector-azure-0 -- -ra + + py313-test-resource-detector-azure-1_ubuntu-latest: + name: resource-detector-azure-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-resource-detector-azure-1 -- -ra + + py314-test-resource-detector-azure-0_ubuntu-latest: + name: resource-detector-azure-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-resource-detector-azure-0 -- -ra + + py314-test-resource-detector-azure-1_ubuntu-latest: + name: resource-detector-azure-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-resource-detector-azure-1 -- -ra + + pypy3-test-resource-detector-azure-0_ubuntu-latest: + name: resource-detector-azure-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-resource-detector-azure-0 -- -ra + + pypy3-test-resource-detector-azure-1_ubuntu-latest: + name: resource-detector-azure-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-resource-detector-azure-1 -- -ra + + py310-test-sdk-extension-aws-0_ubuntu-latest: + name: sdk-extension-aws-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-sdk-extension-aws-0 -- -ra + + py310-test-sdk-extension-aws-1_ubuntu-latest: + name: sdk-extension-aws-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-sdk-extension-aws-1 -- -ra + + py311-test-sdk-extension-aws-0_ubuntu-latest: + name: sdk-extension-aws-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-sdk-extension-aws-0 -- -ra + + py311-test-sdk-extension-aws-1_ubuntu-latest: + name: sdk-extension-aws-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-sdk-extension-aws-1 -- -ra + + py312-test-sdk-extension-aws-0_ubuntu-latest: + name: sdk-extension-aws-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-sdk-extension-aws-0 -- -ra + + py312-test-sdk-extension-aws-1_ubuntu-latest: + name: sdk-extension-aws-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-sdk-extension-aws-1 -- -ra + + py313-test-sdk-extension-aws-0_ubuntu-latest: + name: sdk-extension-aws-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-sdk-extension-aws-0 -- -ra + + py313-test-sdk-extension-aws-1_ubuntu-latest: + name: sdk-extension-aws-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-sdk-extension-aws-1 -- -ra + + py314-test-sdk-extension-aws-0_ubuntu-latest: + name: sdk-extension-aws-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-sdk-extension-aws-0 -- -ra + + py314-test-sdk-extension-aws-1_ubuntu-latest: + name: sdk-extension-aws-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-sdk-extension-aws-1 -- -ra + + pypy3-test-sdk-extension-aws-0_ubuntu-latest: + name: sdk-extension-aws-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-sdk-extension-aws-0 -- -ra + + pypy3-test-sdk-extension-aws-1_ubuntu-latest: + name: sdk-extension-aws-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-sdk-extension-aws-1 -- -ra + + py310-test-distro_ubuntu-latest: + name: distro 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-distro -- -ra + + py311-test-distro_ubuntu-latest: + name: distro 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-distro -- -ra + + py312-test-distro_ubuntu-latest: + name: distro 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-distro -- -ra + + py313-test-distro_ubuntu-latest: + name: distro 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-distro -- -ra + + py314-test-distro_ubuntu-latest: + name: distro 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-distro -- -ra + + pypy3-test-distro_ubuntu-latest: + name: distro pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-distro -- -ra + + py310-test-opentelemetry-instrumentation-wrapt1_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-opentelemetry-instrumentation-wrapt1 -- -ra + + py310-test-opentelemetry-instrumentation-wrapt2_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-opentelemetry-instrumentation-wrapt2 -- -ra + + py311-test-opentelemetry-instrumentation-wrapt1_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-opentelemetry-instrumentation-wrapt1 -- -ra + + py311-test-opentelemetry-instrumentation-wrapt2_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-opentelemetry-instrumentation-wrapt2 -- -ra + + py312-test-opentelemetry-instrumentation-wrapt1_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-opentelemetry-instrumentation-wrapt1 -- -ra + + py312-test-opentelemetry-instrumentation-wrapt2_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-opentelemetry-instrumentation-wrapt2 -- -ra + + py313-test-opentelemetry-instrumentation-wrapt1_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-opentelemetry-instrumentation-wrapt1 -- -ra + + py313-test-opentelemetry-instrumentation-wrapt2_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-opentelemetry-instrumentation-wrapt2 -- -ra + + py314-test-opentelemetry-instrumentation-wrapt1_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-opentelemetry-instrumentation-wrapt1 -- -ra + + py314-test-opentelemetry-instrumentation-wrapt2_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-opentelemetry-instrumentation-wrapt2 -- -ra + + pypy3-test-opentelemetry-instrumentation-wrapt2_ubuntu-latest: + name: opentelemetry-instrumentation-wrapt2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-opentelemetry-instrumentation-wrapt2 -- -ra + + py310-test-instrumentation-aiohttp-client_ubuntu-latest: + name: instrumentation-aiohttp-client 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aiohttp-client -- -ra + + py311-test-instrumentation-aiohttp-client_ubuntu-latest: + name: instrumentation-aiohttp-client 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aiohttp-client -- -ra + + py312-test-instrumentation-aiohttp-client_ubuntu-latest: + name: instrumentation-aiohttp-client 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aiohttp-client -- -ra + + py313-test-instrumentation-aiohttp-client_ubuntu-latest: + name: instrumentation-aiohttp-client 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aiohttp-client -- -ra + + py314-test-instrumentation-aiohttp-client_ubuntu-latest: + name: instrumentation-aiohttp-client 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aiohttp-client -- -ra + + pypy3-test-instrumentation-aiohttp-client_ubuntu-latest: + name: instrumentation-aiohttp-client pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aiohttp-client -- -ra + + py310-test-instrumentation-aiohttp-server_ubuntu-latest: + name: instrumentation-aiohttp-server 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aiohttp-server -- -ra + + py311-test-instrumentation-aiohttp-server_ubuntu-latest: + name: instrumentation-aiohttp-server 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aiohttp-server -- -ra + + py312-test-instrumentation-aiohttp-server_ubuntu-latest: + name: instrumentation-aiohttp-server 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aiohttp-server -- -ra + + py313-test-instrumentation-aiohttp-server_ubuntu-latest: + name: instrumentation-aiohttp-server 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aiohttp-server -- -ra + + py314-test-instrumentation-aiohttp-server_ubuntu-latest: + name: instrumentation-aiohttp-server 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aiohttp-server -- -ra + + pypy3-test-instrumentation-aiohttp-server_ubuntu-latest: + name: instrumentation-aiohttp-server pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aiohttp-server -- -ra + + py310-test-instrumentation-aiopg-wrapt1_ubuntu-latest: + name: instrumentation-aiopg-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aiopg-wrapt1 -- -ra + + py310-test-instrumentation-aiopg-wrapt2_ubuntu-latest: + name: instrumentation-aiopg-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aiopg-wrapt2 -- -ra + + py311-test-instrumentation-aiopg-wrapt1_ubuntu-latest: + name: instrumentation-aiopg-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aiopg-wrapt1 -- -ra + + py311-test-instrumentation-aiopg-wrapt2_ubuntu-latest: + name: instrumentation-aiopg-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aiopg-wrapt2 -- -ra + + py312-test-instrumentation-aiopg-wrapt1_ubuntu-latest: + name: instrumentation-aiopg-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aiopg-wrapt1 -- -ra + + py312-test-instrumentation-aiopg-wrapt2_ubuntu-latest: + name: instrumentation-aiopg-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aiopg-wrapt2 -- -ra + + py313-test-instrumentation-aiopg-wrapt1_ubuntu-latest: + name: instrumentation-aiopg-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aiopg-wrapt1 -- -ra + + py313-test-instrumentation-aiopg-wrapt2_ubuntu-latest: + name: instrumentation-aiopg-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aiopg-wrapt2 -- -ra + + py314-test-instrumentation-aiopg-wrapt1_ubuntu-latest: + name: instrumentation-aiopg-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aiopg-wrapt1 -- -ra + + py314-test-instrumentation-aiopg-wrapt2_ubuntu-latest: + name: instrumentation-aiopg-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aiopg-wrapt2 -- -ra + + py310-test-instrumentation-aws-lambda_ubuntu-latest: + name: instrumentation-aws-lambda 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aws-lambda -- -ra + + py311-test-instrumentation-aws-lambda_ubuntu-latest: + name: instrumentation-aws-lambda 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aws-lambda -- -ra + + py312-test-instrumentation-aws-lambda_ubuntu-latest: + name: instrumentation-aws-lambda 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aws-lambda -- -ra + + py313-test-instrumentation-aws-lambda_ubuntu-latest: + name: instrumentation-aws-lambda 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aws-lambda -- -ra + + py314-test-instrumentation-aws-lambda_ubuntu-latest: + name: instrumentation-aws-lambda 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aws-lambda -- -ra + + pypy3-test-instrumentation-aws-lambda_ubuntu-latest: + name: instrumentation-aws-lambda pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aws-lambda -- -ra + + py310-test-instrumentation-botocore-0-wrapt1_ubuntu-latest: + name: instrumentation-botocore-0-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-0-wrapt1 -- -ra + + py310-test-instrumentation-botocore-0-wrapt2_ubuntu-latest: + name: instrumentation-botocore-0-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-0-wrapt2 -- -ra + + py310-test-instrumentation-botocore-1-wrapt1_ubuntu-latest: + name: instrumentation-botocore-1-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-1-wrapt1 -- -ra + + py310-test-instrumentation-botocore-1-wrapt2_ubuntu-latest: + name: instrumentation-botocore-1-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-1-wrapt2 -- -ra + + py311-test-instrumentation-botocore-0-wrapt1_ubuntu-latest: + name: instrumentation-botocore-0-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-botocore-0-wrapt1 -- -ra + + py311-test-instrumentation-botocore-0-wrapt2_ubuntu-latest: + name: instrumentation-botocore-0-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-botocore-0-wrapt2 -- -ra + + py311-test-instrumentation-botocore-1-wrapt1_ubuntu-latest: + name: instrumentation-botocore-1-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-botocore-1-wrapt1 -- -ra + + py311-test-instrumentation-botocore-1-wrapt2_ubuntu-latest: + name: instrumentation-botocore-1-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-botocore-1-wrapt2 -- -ra + + py312-test-instrumentation-botocore-0-wrapt1_ubuntu-latest: + name: instrumentation-botocore-0-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-botocore-0-wrapt1 -- -ra + + py312-test-instrumentation-botocore-0-wrapt2_ubuntu-latest: + name: instrumentation-botocore-0-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-botocore-0-wrapt2 -- -ra + + py312-test-instrumentation-botocore-1-wrapt1_ubuntu-latest: + name: instrumentation-botocore-1-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-botocore-1-wrapt1 -- -ra + + py312-test-instrumentation-botocore-1-wrapt2_ubuntu-latest: + name: instrumentation-botocore-1-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-botocore-1-wrapt2 -- -ra + + py313-test-instrumentation-botocore-0-wrapt1_ubuntu-latest: + name: instrumentation-botocore-0-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-botocore-0-wrapt1 -- -ra + + py313-test-instrumentation-botocore-0-wrapt2_ubuntu-latest: + name: instrumentation-botocore-0-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-botocore-0-wrapt2 -- -ra + + py313-test-instrumentation-botocore-1-wrapt1_ubuntu-latest: + name: instrumentation-botocore-1-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-botocore-1-wrapt1 -- -ra + + py313-test-instrumentation-botocore-1-wrapt2_ubuntu-latest: + name: instrumentation-botocore-1-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-botocore-1-wrapt2 -- -ra + + py314-test-instrumentation-botocore-0-wrapt1_ubuntu-latest: + name: instrumentation-botocore-0-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-botocore-0-wrapt1 -- -ra + + py314-test-instrumentation-botocore-0-wrapt2_ubuntu-latest: + name: instrumentation-botocore-0-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-botocore-0-wrapt2 -- -ra + + py314-test-instrumentation-botocore-1-wrapt1_ubuntu-latest: + name: instrumentation-botocore-1-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-botocore-1-wrapt1 -- -ra + + py314-test-instrumentation-botocore-1-wrapt2_ubuntu-latest: + name: instrumentation-botocore-1-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-botocore-1-wrapt2 -- -ra + + py310-test-instrumentation-botocore-2_ubuntu-latest: + name: instrumentation-botocore-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-2 -- -ra + + py310-test-instrumentation-botocore-3_ubuntu-latest: + name: instrumentation-botocore-3 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-botocore-3 -- -ra + + py311-test-instrumentation-botocore-2_ubuntu-latest: + name: instrumentation-botocore-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-botocore-2 -- -ra + + py311-test-instrumentation-botocore-3_ubuntu-latest: + name: instrumentation-botocore-3 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-botocore-3 -- -ra + + py312-test-instrumentation-botocore-2_ubuntu-latest: + name: instrumentation-botocore-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-botocore-2 -- -ra + + py312-test-instrumentation-botocore-3_ubuntu-latest: + name: instrumentation-botocore-3 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-botocore-3 -- -ra + + py313-test-instrumentation-botocore-2_ubuntu-latest: + name: instrumentation-botocore-2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-botocore-2 -- -ra + + py313-test-instrumentation-botocore-3_ubuntu-latest: + name: instrumentation-botocore-3 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-botocore-3 -- -ra + + py314-test-instrumentation-botocore-2_ubuntu-latest: + name: instrumentation-botocore-2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-botocore-2 -- -ra + + py314-test-instrumentation-botocore-3_ubuntu-latest: + name: instrumentation-botocore-3 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-botocore-3 -- -ra + + py310-test-instrumentation-boto3sqs_ubuntu-latest: + name: instrumentation-boto3sqs 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-boto3sqs -- -ra + + py311-test-instrumentation-boto3sqs_ubuntu-latest: + name: instrumentation-boto3sqs 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-boto3sqs -- -ra + + py312-test-instrumentation-boto3sqs_ubuntu-latest: + name: instrumentation-boto3sqs 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-boto3sqs -- -ra + + py313-test-instrumentation-boto3sqs_ubuntu-latest: + name: instrumentation-boto3sqs 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-boto3sqs -- -ra + + py314-test-instrumentation-boto3sqs_ubuntu-latest: + name: instrumentation-boto3sqs 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-boto3sqs -- -ra + + pypy3-test-instrumentation-boto3sqs_ubuntu-latest: + name: instrumentation-boto3sqs pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-boto3sqs -- -ra + + py310-test-instrumentation-django-0_ubuntu-latest: + name: instrumentation-django-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-django-0 -- -ra + + py310-test-instrumentation-django-1_ubuntu-latest: + name: instrumentation-django-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-django-1 -- -ra + + py310-test-instrumentation-django-2_ubuntu-latest: + name: instrumentation-django-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-django-2 -- -ra + + py310-test-instrumentation-django-3_ubuntu-latest: + name: instrumentation-django-3 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-django-3 -- -ra + + py311-test-instrumentation-django-0_ubuntu-latest: + name: instrumentation-django-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-django-0 -- -ra + + py311-test-instrumentation-django-1_ubuntu-latest: + name: instrumentation-django-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-django-1 -- -ra + + py311-test-instrumentation-django-2_ubuntu-latest: + name: instrumentation-django-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-django-2 -- -ra + + py311-test-instrumentation-django-3_ubuntu-latest: + name: instrumentation-django-3 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-django-3 -- -ra + + py312-test-instrumentation-django-0_ubuntu-latest: + name: instrumentation-django-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-django-0 -- -ra + + py312-test-instrumentation-django-1_ubuntu-latest: + name: instrumentation-django-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-django-1 -- -ra + + py312-test-instrumentation-django-2_ubuntu-latest: + name: instrumentation-django-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-django-2 -- -ra + + py312-test-instrumentation-django-3_ubuntu-latest: + name: instrumentation-django-3 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-django-3 -- -ra + + py313-test-instrumentation-django-3_ubuntu-latest: + name: instrumentation-django-3 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-django-3 -- -ra + + py314-test-instrumentation-django-3_ubuntu-latest: + name: instrumentation-django-3 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-django-3 -- -ra + + pypy3-test-instrumentation-django-0_ubuntu-latest: + name: instrumentation-django-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-django-0 -- -ra + + pypy3-test-instrumentation-django-1_ubuntu-latest: + name: instrumentation-django-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-django-1 -- -ra + + pypy3-test-instrumentation-django-2_ubuntu-latest: + name: instrumentation-django-2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-django-2 -- -ra + + pypy3-test-instrumentation-django-3_ubuntu-latest: + name: instrumentation-django-3 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-django-3 -- -ra + + py310-test-instrumentation-dbapi-wrapt1_ubuntu-latest: + name: instrumentation-dbapi-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-dbapi-wrapt1 -- -ra + + py310-test-instrumentation-dbapi-wrapt2_ubuntu-latest: + name: instrumentation-dbapi-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-dbapi-wrapt2 -- -ra + + py311-test-instrumentation-dbapi-wrapt1_ubuntu-latest: + name: instrumentation-dbapi-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-dbapi-wrapt1 -- -ra + + py311-test-instrumentation-dbapi-wrapt2_ubuntu-latest: + name: instrumentation-dbapi-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-dbapi-wrapt2 -- -ra + + py312-test-instrumentation-dbapi-wrapt1_ubuntu-latest: + name: instrumentation-dbapi-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-dbapi-wrapt1 -- -ra + + py312-test-instrumentation-dbapi-wrapt2_ubuntu-latest: + name: instrumentation-dbapi-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-dbapi-wrapt2 -- -ra + + py313-test-instrumentation-dbapi-wrapt1_ubuntu-latest: + name: instrumentation-dbapi-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-dbapi-wrapt1 -- -ra + + py313-test-instrumentation-dbapi-wrapt2_ubuntu-latest: + name: instrumentation-dbapi-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-dbapi-wrapt2 -- -ra + + py314-test-instrumentation-dbapi-wrapt1_ubuntu-latest: + name: instrumentation-dbapi-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-dbapi-wrapt1 -- -ra + + py314-test-instrumentation-dbapi-wrapt2_ubuntu-latest: + name: instrumentation-dbapi-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-dbapi-wrapt2 -- -ra + + pypy3-test-instrumentation-dbapi-wrapt2_ubuntu-latest: + name: instrumentation-dbapi-wrapt2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-dbapi-wrapt2 -- -ra + + py310-test-instrumentation-asyncclick_ubuntu-latest: + name: instrumentation-asyncclick 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-asyncclick -- -ra + + py311-test-instrumentation-asyncclick_ubuntu-latest: + name: instrumentation-asyncclick 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-asyncclick -- -ra + + py312-test-instrumentation-asyncclick_ubuntu-latest: + name: instrumentation-asyncclick 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-asyncclick -- -ra + + py313-test-instrumentation-asyncclick_ubuntu-latest: + name: instrumentation-asyncclick 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-asyncclick -- -ra + + py314-test-instrumentation-asyncclick_ubuntu-latest: + name: instrumentation-asyncclick 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-asyncclick -- -ra + + pypy3-test-instrumentation-asyncclick_ubuntu-latest: + name: instrumentation-asyncclick pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-asyncclick -- -ra + + py310-test-instrumentation-click_ubuntu-latest: + name: instrumentation-click 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-click -- -ra + + py311-test-instrumentation-click_ubuntu-latest: + name: instrumentation-click 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-click -- -ra + + py312-test-instrumentation-click_ubuntu-latest: + name: instrumentation-click 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-click -- -ra + + py313-test-instrumentation-click_ubuntu-latest: + name: instrumentation-click 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-click -- -ra + + py314-test-instrumentation-click_ubuntu-latest: + name: instrumentation-click 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-click -- -ra + + pypy3-test-instrumentation-click_ubuntu-latest: + name: instrumentation-click pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-click -- -ra + + py310-test-instrumentation-elasticsearch-0_ubuntu-latest: + name: instrumentation-elasticsearch-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-elasticsearch-0 -- -ra + + py310-test-instrumentation-elasticsearch-1_ubuntu-latest: + name: instrumentation-elasticsearch-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-elasticsearch-1 -- -ra + + py310-test-instrumentation-elasticsearch-2_ubuntu-latest: + name: instrumentation-elasticsearch-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-elasticsearch-2 -- -ra + + py311-test-instrumentation-elasticsearch-0_ubuntu-latest: + name: instrumentation-elasticsearch-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-elasticsearch-0 -- -ra + + py311-test-instrumentation-elasticsearch-1_ubuntu-latest: + name: instrumentation-elasticsearch-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-elasticsearch-1 -- -ra + + py311-test-instrumentation-elasticsearch-2_ubuntu-latest: + name: instrumentation-elasticsearch-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-elasticsearch-2 -- -ra + + py312-test-instrumentation-elasticsearch-0_ubuntu-latest: + name: instrumentation-elasticsearch-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-elasticsearch-0 -- -ra + + py312-test-instrumentation-elasticsearch-1_ubuntu-latest: + name: instrumentation-elasticsearch-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-elasticsearch-1 -- -ra + + py312-test-instrumentation-elasticsearch-2_ubuntu-latest: + name: instrumentation-elasticsearch-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-elasticsearch-2 -- -ra + + py313-test-instrumentation-elasticsearch-0_ubuntu-latest: + name: instrumentation-elasticsearch-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-elasticsearch-0 -- -ra + + py313-test-instrumentation-elasticsearch-1_ubuntu-latest: + name: instrumentation-elasticsearch-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-elasticsearch-1 -- -ra + + py313-test-instrumentation-elasticsearch-2_ubuntu-latest: + name: instrumentation-elasticsearch-2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-elasticsearch-2 -- -ra + + py314-test-instrumentation-elasticsearch-0_ubuntu-latest: + name: instrumentation-elasticsearch-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-elasticsearch-0 -- -ra + + py314-test-instrumentation-elasticsearch-1_ubuntu-latest: + name: instrumentation-elasticsearch-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-elasticsearch-1 -- -ra + + py314-test-instrumentation-elasticsearch-2_ubuntu-latest: + name: instrumentation-elasticsearch-2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-elasticsearch-2 -- -ra + + pypy3-test-instrumentation-elasticsearch-0_ubuntu-latest: + name: instrumentation-elasticsearch-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-elasticsearch-0 -- -ra + + pypy3-test-instrumentation-elasticsearch-1_ubuntu-latest: + name: instrumentation-elasticsearch-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-elasticsearch-1 -- -ra + + pypy3-test-instrumentation-elasticsearch-2_ubuntu-latest: + name: instrumentation-elasticsearch-2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-elasticsearch-2 -- -ra + + py310-test-instrumentation-falcon-1_ubuntu-latest: + name: instrumentation-falcon-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-falcon-1 -- -ra + + py310-test-instrumentation-falcon-2_ubuntu-latest: + name: instrumentation-falcon-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-falcon-2 -- -ra + + py310-test-instrumentation-falcon-3_ubuntu-latest: + name: instrumentation-falcon-3 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-falcon-3 -- -ra + + py310-test-instrumentation-falcon-4_ubuntu-latest: + name: instrumentation-falcon-4 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-falcon-4 -- -ra + + py311-test-instrumentation-falcon-1_ubuntu-latest: + name: instrumentation-falcon-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-falcon-1 -- -ra + + py311-test-instrumentation-falcon-2_ubuntu-latest: + name: instrumentation-falcon-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-falcon-2 -- -ra + + py311-test-instrumentation-falcon-3_ubuntu-latest: + name: instrumentation-falcon-3 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-falcon-3 -- -ra + + py311-test-instrumentation-falcon-4_ubuntu-latest: + name: instrumentation-falcon-4 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-falcon-4 -- -ra + + py312-test-instrumentation-falcon-1_ubuntu-latest: + name: instrumentation-falcon-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-falcon-1 -- -ra + + py312-test-instrumentation-falcon-2_ubuntu-latest: + name: instrumentation-falcon-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-falcon-2 -- -ra + + py312-test-instrumentation-falcon-3_ubuntu-latest: + name: instrumentation-falcon-3 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-falcon-3 -- -ra + + py312-test-instrumentation-falcon-4_ubuntu-latest: + name: instrumentation-falcon-4 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-falcon-4 -- -ra + + py313-test-instrumentation-falcon-4_ubuntu-latest: + name: instrumentation-falcon-4 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-falcon-4 -- -ra + + py314-test-instrumentation-falcon-4_ubuntu-latest: + name: instrumentation-falcon-4 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-falcon-4 -- -ra + + pypy3-test-instrumentation-falcon-1_ubuntu-latest: + name: instrumentation-falcon-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-falcon-1 -- -ra + + pypy3-test-instrumentation-falcon-2_ubuntu-latest: + name: instrumentation-falcon-2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-falcon-2 -- -ra + + pypy3-test-instrumentation-falcon-3_ubuntu-latest: + name: instrumentation-falcon-3 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-falcon-3 -- -ra + + pypy3-test-instrumentation-falcon-4_ubuntu-latest: + name: instrumentation-falcon-4 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-falcon-4 -- -ra + + py310-test-instrumentation-fastapi_ubuntu-latest: + name: instrumentation-fastapi 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-fastapi -- -ra + + py311-test-instrumentation-fastapi_ubuntu-latest: + name: instrumentation-fastapi 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-fastapi -- -ra + + py312-test-instrumentation-fastapi_ubuntu-latest: + name: instrumentation-fastapi 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-fastapi -- -ra + + py313-test-instrumentation-fastapi_ubuntu-latest: + name: instrumentation-fastapi 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-fastapi -- -ra + + py314-test-instrumentation-fastapi_ubuntu-latest: + name: instrumentation-fastapi 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-fastapi -- -ra + + pypy3-test-instrumentation-fastapi_ubuntu-latest: + name: instrumentation-fastapi pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-fastapi -- -ra + + py310-test-instrumentation-flask-0_ubuntu-latest: + name: instrumentation-flask-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-flask-0 -- -ra + + py310-test-instrumentation-flask-1_ubuntu-latest: + name: instrumentation-flask-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-flask-1 -- -ra + + py310-test-instrumentation-flask-2_ubuntu-latest: + name: instrumentation-flask-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-flask-2 -- -ra + + py310-test-instrumentation-flask-3_ubuntu-latest: + name: instrumentation-flask-3 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-flask-3 -- -ra + + py311-test-instrumentation-flask-0_ubuntu-latest: + name: instrumentation-flask-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-flask-0 -- -ra + + py311-test-instrumentation-flask-1_ubuntu-latest: + name: instrumentation-flask-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-flask-1 -- -ra + + py311-test-instrumentation-flask-2_ubuntu-latest: + name: instrumentation-flask-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-flask-2 -- -ra + + py311-test-instrumentation-flask-3_ubuntu-latest: + name: instrumentation-flask-3 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-flask-3 -- -ra + + py312-test-instrumentation-flask-0_ubuntu-latest: + name: instrumentation-flask-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-flask-0 -- -ra + + py312-test-instrumentation-flask-1_ubuntu-latest: + name: instrumentation-flask-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-flask-1 -- -ra + + py312-test-instrumentation-flask-2_ubuntu-latest: + name: instrumentation-flask-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-flask-2 -- -ra + + py312-test-instrumentation-flask-3_ubuntu-latest: + name: instrumentation-flask-3 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-flask-3 -- -ra + + py313-test-instrumentation-flask-0_ubuntu-latest: + name: instrumentation-flask-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-flask-0 -- -ra + + py313-test-instrumentation-flask-1_ubuntu-latest: + name: instrumentation-flask-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-flask-1 -- -ra + + py313-test-instrumentation-flask-2_ubuntu-latest: + name: instrumentation-flask-2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-flask-2 -- -ra + + py313-test-instrumentation-flask-3_ubuntu-latest: + name: instrumentation-flask-3 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-flask-3 -- -ra + + py314-test-instrumentation-flask-0_ubuntu-latest: + name: instrumentation-flask-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-flask-0 -- -ra + + py314-test-instrumentation-flask-1_ubuntu-latest: + name: instrumentation-flask-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-flask-1 -- -ra + + py314-test-instrumentation-flask-2_ubuntu-latest: + name: instrumentation-flask-2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-flask-2 -- -ra + + py314-test-instrumentation-flask-3_ubuntu-latest: + name: instrumentation-flask-3 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-flask-3 -- -ra + + pypy3-test-instrumentation-flask-0_ubuntu-latest: + name: instrumentation-flask-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-flask-0 -- -ra + + pypy3-test-instrumentation-flask-1_ubuntu-latest: + name: instrumentation-flask-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-flask-1 -- -ra + + py310-test-instrumentation-urllib_ubuntu-latest: + name: instrumentation-urllib 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-urllib -- -ra + + py311-test-instrumentation-urllib_ubuntu-latest: + name: instrumentation-urllib 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-urllib -- -ra + + py312-test-instrumentation-urllib_ubuntu-latest: + name: instrumentation-urllib 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-urllib -- -ra + + py313-test-instrumentation-urllib_ubuntu-latest: + name: instrumentation-urllib 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-urllib -- -ra + + py314-test-instrumentation-urllib_ubuntu-latest: + name: instrumentation-urllib 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-urllib -- -ra + + pypy3-test-instrumentation-urllib_ubuntu-latest: + name: instrumentation-urllib pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-urllib -- -ra + + py310-test-instrumentation-urllib3-0_ubuntu-latest: + name: instrumentation-urllib3-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-urllib3-0 -- -ra + + py310-test-instrumentation-urllib3-1_ubuntu-latest: + name: instrumentation-urllib3-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-urllib3-1 -- -ra + + py311-test-instrumentation-urllib3-0_ubuntu-latest: + name: instrumentation-urllib3-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-urllib3-0 -- -ra + + py311-test-instrumentation-urllib3-1_ubuntu-latest: + name: instrumentation-urllib3-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-urllib3-1 -- -ra + + py312-test-instrumentation-urllib3-0_ubuntu-latest: + name: instrumentation-urllib3-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-urllib3-0 -- -ra + + py312-test-instrumentation-urllib3-1_ubuntu-latest: + name: instrumentation-urllib3-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-urllib3-1 -- -ra + + py313-test-instrumentation-urllib3-0_ubuntu-latest: + name: instrumentation-urllib3-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-urllib3-0 -- -ra + + py313-test-instrumentation-urllib3-1_ubuntu-latest: + name: instrumentation-urllib3-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-urllib3-1 -- -ra + + pypy3-test-instrumentation-urllib3-0_ubuntu-latest: + name: instrumentation-urllib3-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-urllib3-0 -- -ra + + pypy3-test-instrumentation-urllib3-1_ubuntu-latest: + name: instrumentation-urllib3-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-urllib3-1 -- -ra + + py310-test-instrumentation-requests_ubuntu-latest: + name: instrumentation-requests 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-requests -- -ra + + py311-test-instrumentation-requests_ubuntu-latest: + name: instrumentation-requests 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-requests -- -ra + + py312-test-instrumentation-requests_ubuntu-latest: + name: instrumentation-requests 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-requests -- -ra + + py313-test-instrumentation-requests_ubuntu-latest: + name: instrumentation-requests 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-requests -- -ra + + py314-test-instrumentation-requests_ubuntu-latest: + name: instrumentation-requests 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-requests -- -ra + + py310-test-instrumentation-starlette-oldest_ubuntu-latest: + name: instrumentation-starlette-oldest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-starlette-oldest -- -ra + + py310-test-instrumentation-starlette-latest_ubuntu-latest: + name: instrumentation-starlette-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-starlette-latest -- -ra + + py311-test-instrumentation-starlette-oldest_ubuntu-latest: + name: instrumentation-starlette-oldest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-starlette-oldest -- -ra + + py311-test-instrumentation-starlette-latest_ubuntu-latest: + name: instrumentation-starlette-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-starlette-latest -- -ra + + py312-test-instrumentation-starlette-oldest_ubuntu-latest: + name: instrumentation-starlette-oldest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-starlette-oldest -- -ra + + py312-test-instrumentation-starlette-latest_ubuntu-latest: + name: instrumentation-starlette-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-starlette-latest -- -ra + + py313-test-instrumentation-starlette-oldest_ubuntu-latest: + name: instrumentation-starlette-oldest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-starlette-oldest -- -ra + + py313-test-instrumentation-starlette-latest_ubuntu-latest: + name: instrumentation-starlette-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-starlette-latest -- -ra + + py314-test-instrumentation-starlette-oldest_ubuntu-latest: + name: instrumentation-starlette-oldest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-starlette-oldest -- -ra + + py314-test-instrumentation-starlette-latest_ubuntu-latest: + name: instrumentation-starlette-latest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-starlette-latest -- -ra + + pypy3-test-instrumentation-starlette-oldest_ubuntu-latest: + name: instrumentation-starlette-oldest pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-starlette-oldest -- -ra + + pypy3-test-instrumentation-starlette-latest_ubuntu-latest: + name: instrumentation-starlette-latest pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-starlette-latest -- -ra + + py310-test-instrumentation-jinja2_ubuntu-latest: + name: instrumentation-jinja2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-jinja2 -- -ra + + py311-test-instrumentation-jinja2_ubuntu-latest: + name: instrumentation-jinja2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-jinja2 -- -ra + + py312-test-instrumentation-jinja2_ubuntu-latest: + name: instrumentation-jinja2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-jinja2 -- -ra + + py313-test-instrumentation-jinja2_ubuntu-latest: + name: instrumentation-jinja2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-jinja2 -- -ra + + py314-test-instrumentation-jinja2_ubuntu-latest: + name: instrumentation-jinja2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-jinja2 -- -ra + + pypy3-test-instrumentation-jinja2_ubuntu-latest: + name: instrumentation-jinja2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-jinja2 -- -ra + + py310-test-instrumentation-logging_ubuntu-latest: + name: instrumentation-logging 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-logging -- -ra + + py311-test-instrumentation-logging_ubuntu-latest: + name: instrumentation-logging 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-logging -- -ra + + py312-test-instrumentation-logging_ubuntu-latest: + name: instrumentation-logging 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-logging -- -ra + + py313-test-instrumentation-logging_ubuntu-latest: + name: instrumentation-logging 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-logging -- -ra + + py314-test-instrumentation-logging_ubuntu-latest: + name: instrumentation-logging 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-logging -- -ra + + pypy3-test-instrumentation-logging_ubuntu-latest: + name: instrumentation-logging pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-logging -- -ra + + py310-test-exporter-richconsole_ubuntu-latest: + name: exporter-richconsole 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-exporter-richconsole -- -ra + + py311-test-exporter-richconsole_ubuntu-latest: + name: exporter-richconsole 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-exporter-richconsole -- -ra + + py312-test-exporter-richconsole_ubuntu-latest: + name: exporter-richconsole 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-exporter-richconsole -- -ra + + py313-test-exporter-richconsole_ubuntu-latest: + name: exporter-richconsole 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-exporter-richconsole -- -ra + + py314-test-exporter-richconsole_ubuntu-latest: + name: exporter-richconsole 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-exporter-richconsole -- -ra + + pypy3-test-exporter-richconsole_ubuntu-latest: + name: exporter-richconsole pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-exporter-richconsole -- -ra + + py310-test-exporter-prometheus-remote-write_ubuntu-latest: + name: exporter-prometheus-remote-write 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-exporter-prometheus-remote-write -- -ra + + py311-test-exporter-prometheus-remote-write_ubuntu-latest: + name: exporter-prometheus-remote-write 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-exporter-prometheus-remote-write -- -ra + + py312-test-exporter-prometheus-remote-write_ubuntu-latest: + name: exporter-prometheus-remote-write 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-exporter-prometheus-remote-write -- -ra + + py313-test-exporter-prometheus-remote-write_ubuntu-latest: + name: exporter-prometheus-remote-write 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-exporter-prometheus-remote-write -- -ra + + py314-test-exporter-prometheus-remote-write_ubuntu-latest: + name: exporter-prometheus-remote-write 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-exporter-prometheus-remote-write -- -ra + + pypy3-test-exporter-prometheus-remote-write_ubuntu-latest: + name: exporter-prometheus-remote-write pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-exporter-prometheus-remote-write -- -ra + + py310-test-instrumentation-mysql-0_ubuntu-latest: + name: instrumentation-mysql-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-mysql-0 -- -ra + + py310-test-instrumentation-mysql-1_ubuntu-latest: + name: instrumentation-mysql-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-mysql-1 -- -ra + + py311-test-instrumentation-mysql-0_ubuntu-latest: + name: instrumentation-mysql-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-mysql-0 -- -ra + + py311-test-instrumentation-mysql-1_ubuntu-latest: + name: instrumentation-mysql-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-mysql-1 -- -ra + + py312-test-instrumentation-mysql-0_ubuntu-latest: + name: instrumentation-mysql-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-mysql-0 -- -ra + + py312-test-instrumentation-mysql-1_ubuntu-latest: + name: instrumentation-mysql-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-mysql-1 -- -ra + + py313-test-instrumentation-mysql-0_ubuntu-latest: + name: instrumentation-mysql-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-mysql-0 -- -ra + + py313-test-instrumentation-mysql-1_ubuntu-latest: + name: instrumentation-mysql-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-mysql-1 -- -ra + + py314-test-instrumentation-mysql-0_ubuntu-latest: + name: instrumentation-mysql-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-mysql-0 -- -ra + + py314-test-instrumentation-mysql-1_ubuntu-latest: + name: instrumentation-mysql-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-mysql-1 -- -ra + + pypy3-test-instrumentation-mysql-0_ubuntu-latest: + name: instrumentation-mysql-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-mysql-0 -- -ra + + pypy3-test-instrumentation-mysql-1_ubuntu-latest: + name: instrumentation-mysql-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-mysql-1 -- -ra + + py310-test-instrumentation-mysqlclient_ubuntu-latest: + name: instrumentation-mysqlclient 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-mysqlclient -- -ra + + py311-test-instrumentation-mysqlclient_ubuntu-latest: + name: instrumentation-mysqlclient 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-mysqlclient -- -ra + + py312-test-instrumentation-mysqlclient_ubuntu-latest: + name: instrumentation-mysqlclient 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-mysqlclient -- -ra + + py313-test-instrumentation-mysqlclient_ubuntu-latest: + name: instrumentation-mysqlclient 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-mysqlclient -- -ra + + py314-test-instrumentation-mysqlclient_ubuntu-latest: + name: instrumentation-mysqlclient 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-mysqlclient -- -ra + + pypy3-test-instrumentation-mysqlclient_ubuntu-latest: + name: instrumentation-mysqlclient pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-mysqlclient -- -ra + + py310-test-instrumentation-psycopg2_ubuntu-latest: + name: instrumentation-psycopg2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-psycopg2 -- -ra + + py311-test-instrumentation-psycopg2_ubuntu-latest: + name: instrumentation-psycopg2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-psycopg2 -- -ra + + py312-test-instrumentation-psycopg2_ubuntu-latest: + name: instrumentation-psycopg2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-psycopg2 -- -ra + + py313-test-instrumentation-psycopg2_ubuntu-latest: + name: instrumentation-psycopg2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-psycopg2 -- -ra + + py314-test-instrumentation-psycopg2_ubuntu-latest: + name: instrumentation-psycopg2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-psycopg2 -- -ra + + py310-test-instrumentation-psycopg2-binary_ubuntu-latest: + name: instrumentation-psycopg2-binary 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-psycopg2-binary -- -ra + + py311-test-instrumentation-psycopg2-binary_ubuntu-latest: + name: instrumentation-psycopg2-binary 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-psycopg2-binary -- -ra + + py312-test-instrumentation-psycopg2-binary_ubuntu-latest: + name: instrumentation-psycopg2-binary 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-psycopg2-binary -- -ra + + py313-test-instrumentation-psycopg2-binary_ubuntu-latest: + name: instrumentation-psycopg2-binary 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-psycopg2-binary -- -ra + + py314-test-instrumentation-psycopg2-binary_ubuntu-latest: + name: instrumentation-psycopg2-binary 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-psycopg2-binary -- -ra + + py310-test-instrumentation-psycopg_ubuntu-latest: + name: instrumentation-psycopg 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-psycopg -- -ra + + py311-test-instrumentation-psycopg_ubuntu-latest: + name: instrumentation-psycopg 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-psycopg -- -ra + + py312-test-instrumentation-psycopg_ubuntu-latest: + name: instrumentation-psycopg 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-psycopg -- -ra + + py313-test-instrumentation-psycopg_ubuntu-latest: + name: instrumentation-psycopg 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-psycopg -- -ra + + py314-test-instrumentation-psycopg_ubuntu-latest: + name: instrumentation-psycopg 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-psycopg -- -ra + + pypy3-test-instrumentation-psycopg_ubuntu-latest: + name: instrumentation-psycopg pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-psycopg -- -ra + + py310-test-instrumentation-pymemcache-0_ubuntu-latest: + name: instrumentation-pymemcache-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymemcache-0 -- -ra + + py310-test-instrumentation-pymemcache-1_ubuntu-latest: + name: instrumentation-pymemcache-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymemcache-1 -- -ra + + py310-test-instrumentation-pymemcache-2_ubuntu-latest: + name: instrumentation-pymemcache-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymemcache-2 -- -ra + + py310-test-instrumentation-pymemcache-3_ubuntu-latest: + name: instrumentation-pymemcache-3 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymemcache-3 -- -ra + + py310-test-instrumentation-pymemcache-4_ubuntu-latest: + name: instrumentation-pymemcache-4 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymemcache-4 -- -ra + + py311-test-instrumentation-pymemcache-0_ubuntu-latest: + name: instrumentation-pymemcache-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymemcache-0 -- -ra + + py311-test-instrumentation-pymemcache-1_ubuntu-latest: + name: instrumentation-pymemcache-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymemcache-1 -- -ra + + py311-test-instrumentation-pymemcache-2_ubuntu-latest: + name: instrumentation-pymemcache-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymemcache-2 -- -ra + + py311-test-instrumentation-pymemcache-3_ubuntu-latest: + name: instrumentation-pymemcache-3 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymemcache-3 -- -ra + + py311-test-instrumentation-pymemcache-4_ubuntu-latest: + name: instrumentation-pymemcache-4 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymemcache-4 -- -ra + + py312-test-instrumentation-pymemcache-0_ubuntu-latest: + name: instrumentation-pymemcache-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymemcache-0 -- -ra + + py312-test-instrumentation-pymemcache-1_ubuntu-latest: + name: instrumentation-pymemcache-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymemcache-1 -- -ra + + py312-test-instrumentation-pymemcache-2_ubuntu-latest: + name: instrumentation-pymemcache-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymemcache-2 -- -ra + + py312-test-instrumentation-pymemcache-3_ubuntu-latest: + name: instrumentation-pymemcache-3 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymemcache-3 -- -ra + + py312-test-instrumentation-pymemcache-4_ubuntu-latest: + name: instrumentation-pymemcache-4 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymemcache-4 -- -ra + + py313-test-instrumentation-pymemcache-0_ubuntu-latest: + name: instrumentation-pymemcache-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymemcache-0 -- -ra + + py313-test-instrumentation-pymemcache-1_ubuntu-latest: + name: instrumentation-pymemcache-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymemcache-1 -- -ra + + py313-test-instrumentation-pymemcache-2_ubuntu-latest: + name: instrumentation-pymemcache-2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymemcache-2 -- -ra + + py313-test-instrumentation-pymemcache-3_ubuntu-latest: + name: instrumentation-pymemcache-3 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymemcache-3 -- -ra + + py313-test-instrumentation-pymemcache-4_ubuntu-latest: + name: instrumentation-pymemcache-4 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymemcache-4 -- -ra + + py314-test-instrumentation-pymemcache-0_ubuntu-latest: + name: instrumentation-pymemcache-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymemcache-0 -- -ra + + py314-test-instrumentation-pymemcache-1_ubuntu-latest: + name: instrumentation-pymemcache-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymemcache-1 -- -ra + + py314-test-instrumentation-pymemcache-2_ubuntu-latest: + name: instrumentation-pymemcache-2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymemcache-2 -- -ra + + py314-test-instrumentation-pymemcache-3_ubuntu-latest: + name: instrumentation-pymemcache-3 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymemcache-3 -- -ra + + py314-test-instrumentation-pymemcache-4_ubuntu-latest: + name: instrumentation-pymemcache-4 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymemcache-4 -- -ra + + pypy3-test-instrumentation-pymemcache-0_ubuntu-latest: + name: instrumentation-pymemcache-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pymemcache-0 -- -ra + + pypy3-test-instrumentation-pymemcache-1_ubuntu-latest: + name: instrumentation-pymemcache-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pymemcache-1 -- -ra + + pypy3-test-instrumentation-pymemcache-2_ubuntu-latest: + name: instrumentation-pymemcache-2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pymemcache-2 -- -ra + + pypy3-test-instrumentation-pymemcache-3_ubuntu-latest: + name: instrumentation-pymemcache-3 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pymemcache-3 -- -ra + + pypy3-test-instrumentation-pymemcache-4_ubuntu-latest: + name: instrumentation-pymemcache-4 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pymemcache-4 -- -ra + + py310-test-instrumentation-pymongo_ubuntu-latest: + name: instrumentation-pymongo 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymongo -- -ra + + py311-test-instrumentation-pymongo_ubuntu-latest: + name: instrumentation-pymongo 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymongo -- -ra + + py312-test-instrumentation-pymongo_ubuntu-latest: + name: instrumentation-pymongo 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymongo -- -ra + + py313-test-instrumentation-pymongo_ubuntu-latest: + name: instrumentation-pymongo 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymongo -- -ra + + py314-test-instrumentation-pymongo_ubuntu-latest: + name: instrumentation-pymongo 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymongo -- -ra + + pypy3-test-instrumentation-pymongo_ubuntu-latest: + name: instrumentation-pymongo pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pymongo -- -ra + + py310-test-instrumentation-pymysql_ubuntu-latest: + name: instrumentation-pymysql 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymysql -- -ra + + py311-test-instrumentation-pymysql_ubuntu-latest: + name: instrumentation-pymysql 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymysql -- -ra + + py312-test-instrumentation-pymysql_ubuntu-latest: + name: instrumentation-pymysql 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymysql -- -ra + + py313-test-instrumentation-pymysql_ubuntu-latest: + name: instrumentation-pymysql 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymysql -- -ra + + py314-test-instrumentation-pymysql_ubuntu-latest: + name: instrumentation-pymysql 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymysql -- -ra + + pypy3-test-instrumentation-pymysql_ubuntu-latest: + name: instrumentation-pymysql pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pymysql -- -ra + + py310-test-instrumentation-pymssql_ubuntu-latest: + name: instrumentation-pymssql 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pymssql -- -ra + + py311-test-instrumentation-pymssql_ubuntu-latest: + name: instrumentation-pymssql 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pymssql -- -ra + + py312-test-instrumentation-pymssql_ubuntu-latest: + name: instrumentation-pymssql 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pymssql -- -ra + + py313-test-instrumentation-pymssql_ubuntu-latest: + name: instrumentation-pymssql 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pymssql -- -ra + + py314-test-instrumentation-pymssql_ubuntu-latest: + name: instrumentation-pymssql 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pymssql -- -ra + + py310-test-instrumentation-pyramid_ubuntu-latest: + name: instrumentation-pyramid 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-pyramid -- -ra + + py311-test-instrumentation-pyramid_ubuntu-latest: + name: instrumentation-pyramid 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-pyramid -- -ra + + py312-test-instrumentation-pyramid_ubuntu-latest: + name: instrumentation-pyramid 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-pyramid -- -ra + + py313-test-instrumentation-pyramid_ubuntu-latest: + name: instrumentation-pyramid 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-pyramid -- -ra + + py314-test-instrumentation-pyramid_ubuntu-latest: + name: instrumentation-pyramid 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-pyramid -- -ra + + pypy3-test-instrumentation-pyramid_ubuntu-latest: + name: instrumentation-pyramid pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-pyramid -- -ra + + py310-test-instrumentation-asgi_ubuntu-latest: + name: instrumentation-asgi 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-asgi -- -ra + + py311-test-instrumentation-asgi_ubuntu-latest: + name: instrumentation-asgi 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-asgi -- -ra + + py312-test-instrumentation-asgi_ubuntu-latest: + name: instrumentation-asgi 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-asgi -- -ra + + py313-test-instrumentation-asgi_ubuntu-latest: + name: instrumentation-asgi 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-asgi -- -ra + + py314-test-instrumentation-asgi_ubuntu-latest: + name: instrumentation-asgi 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-asgi -- -ra + + pypy3-test-instrumentation-asgi_ubuntu-latest: + name: instrumentation-asgi pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-asgi -- -ra + + py310-test-instrumentation-asyncpg-wrapt1_ubuntu-latest: + name: instrumentation-asyncpg-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-asyncpg-wrapt1 -- -ra + + py310-test-instrumentation-asyncpg-wrapt2_ubuntu-latest: + name: instrumentation-asyncpg-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-asyncpg-wrapt2 -- -ra + + py311-test-instrumentation-asyncpg-wrapt1_ubuntu-latest: + name: instrumentation-asyncpg-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-asyncpg-wrapt1 -- -ra + + py311-test-instrumentation-asyncpg-wrapt2_ubuntu-latest: + name: instrumentation-asyncpg-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-asyncpg-wrapt2 -- -ra + + py312-test-instrumentation-asyncpg-wrapt1_ubuntu-latest: + name: instrumentation-asyncpg-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-asyncpg-wrapt1 -- -ra + + py312-test-instrumentation-asyncpg-wrapt2_ubuntu-latest: + name: instrumentation-asyncpg-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-asyncpg-wrapt2 -- -ra + + py313-test-instrumentation-asyncpg-wrapt1_ubuntu-latest: + name: instrumentation-asyncpg-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-asyncpg-wrapt1 -- -ra + + py313-test-instrumentation-asyncpg-wrapt2_ubuntu-latest: + name: instrumentation-asyncpg-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-asyncpg-wrapt2 -- -ra + + py314-test-instrumentation-asyncpg-wrapt1_ubuntu-latest: + name: instrumentation-asyncpg-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-asyncpg-wrapt1 -- -ra + + py314-test-instrumentation-asyncpg-wrapt2_ubuntu-latest: + name: instrumentation-asyncpg-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-asyncpg-wrapt2 -- -ra + + py310-test-instrumentation-sqlite3_ubuntu-latest: + name: instrumentation-sqlite3 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sqlite3 -- -ra + + py311-test-instrumentation-sqlite3_ubuntu-latest: + name: instrumentation-sqlite3 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-sqlite3 -- -ra + + py312-test-instrumentation-sqlite3_ubuntu-latest: + name: instrumentation-sqlite3 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-sqlite3 -- -ra + + py313-test-instrumentation-sqlite3_ubuntu-latest: + name: instrumentation-sqlite3 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-sqlite3 -- -ra + + py314-test-instrumentation-sqlite3_ubuntu-latest: + name: instrumentation-sqlite3 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-sqlite3 -- -ra + + pypy3-test-instrumentation-sqlite3_ubuntu-latest: + name: instrumentation-sqlite3 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-sqlite3 -- -ra + + py310-test-instrumentation-wsgi_ubuntu-latest: + name: instrumentation-wsgi 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-wsgi -- -ra + + py311-test-instrumentation-wsgi_ubuntu-latest: + name: instrumentation-wsgi 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-wsgi -- -ra + + py312-test-instrumentation-wsgi_ubuntu-latest: + name: instrumentation-wsgi 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-wsgi -- -ra + + py313-test-instrumentation-wsgi_ubuntu-latest: + name: instrumentation-wsgi 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-wsgi -- -ra + + py314-test-instrumentation-wsgi_ubuntu-latest: + name: instrumentation-wsgi 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-wsgi -- -ra + + pypy3-test-instrumentation-wsgi_ubuntu-latest: + name: instrumentation-wsgi pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-wsgi -- -ra + + py310-test-instrumentation-grpc-0-wrapt1_ubuntu-latest: + name: instrumentation-grpc-0-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-grpc-0-wrapt1 -- -ra + + py310-test-instrumentation-grpc-0-wrapt2_ubuntu-latest: + name: instrumentation-grpc-0-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-grpc-0-wrapt2 -- -ra + + py310-test-instrumentation-grpc-1-wrapt1_ubuntu-latest: + name: instrumentation-grpc-1-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-grpc-1-wrapt1 -- -ra + + py310-test-instrumentation-grpc-1-wrapt2_ubuntu-latest: + name: instrumentation-grpc-1-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-grpc-1-wrapt2 -- -ra + + py311-test-instrumentation-grpc-0-wrapt1_ubuntu-latest: + name: instrumentation-grpc-0-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-grpc-0-wrapt1 -- -ra + + py311-test-instrumentation-grpc-0-wrapt2_ubuntu-latest: + name: instrumentation-grpc-0-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-grpc-0-wrapt2 -- -ra + + py311-test-instrumentation-grpc-1-wrapt1_ubuntu-latest: + name: instrumentation-grpc-1-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-grpc-1-wrapt1 -- -ra + + py311-test-instrumentation-grpc-1-wrapt2_ubuntu-latest: + name: instrumentation-grpc-1-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-grpc-1-wrapt2 -- -ra + + py312-test-instrumentation-grpc-0-wrapt1_ubuntu-latest: + name: instrumentation-grpc-0-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-grpc-0-wrapt1 -- -ra + + py312-test-instrumentation-grpc-0-wrapt2_ubuntu-latest: + name: instrumentation-grpc-0-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-grpc-0-wrapt2 -- -ra + + py312-test-instrumentation-grpc-1-wrapt1_ubuntu-latest: + name: instrumentation-grpc-1-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-grpc-1-wrapt1 -- -ra + + py312-test-instrumentation-grpc-1-wrapt2_ubuntu-latest: + name: instrumentation-grpc-1-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-grpc-1-wrapt2 -- -ra + + py313-test-instrumentation-grpc-1-wrapt1_ubuntu-latest: + name: instrumentation-grpc-1-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-grpc-1-wrapt1 -- -ra + + py313-test-instrumentation-grpc-1-wrapt2_ubuntu-latest: + name: instrumentation-grpc-1-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-grpc-1-wrapt2 -- -ra + + py314-test-instrumentation-grpc-1-wrapt1_ubuntu-latest: + name: instrumentation-grpc-1-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-grpc-1-wrapt1 -- -ra + + py314-test-instrumentation-grpc-1-wrapt2_ubuntu-latest: + name: instrumentation-grpc-1-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-grpc-1-wrapt2 -- -ra + + py310-test-instrumentation-sqlalchemy-1_ubuntu-latest: + name: instrumentation-sqlalchemy-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sqlalchemy-1 -- -ra + + py310-test-instrumentation-sqlalchemy-2_ubuntu-latest: + name: instrumentation-sqlalchemy-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sqlalchemy-2 -- -ra + + py311-test-instrumentation-sqlalchemy-1_ubuntu-latest: + name: instrumentation-sqlalchemy-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-sqlalchemy-1 -- -ra + + py311-test-instrumentation-sqlalchemy-2_ubuntu-latest: + name: instrumentation-sqlalchemy-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-sqlalchemy-2 -- -ra + + py312-test-instrumentation-sqlalchemy-1_ubuntu-latest: + name: instrumentation-sqlalchemy-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-sqlalchemy-1 -- -ra + + py312-test-instrumentation-sqlalchemy-2_ubuntu-latest: + name: instrumentation-sqlalchemy-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-sqlalchemy-2 -- -ra + + py313-test-instrumentation-sqlalchemy-1_ubuntu-latest: + name: instrumentation-sqlalchemy-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-sqlalchemy-1 -- -ra + + py313-test-instrumentation-sqlalchemy-2_ubuntu-latest: + name: instrumentation-sqlalchemy-2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-sqlalchemy-2 -- -ra + + py314-test-instrumentation-sqlalchemy-2_ubuntu-latest: + name: instrumentation-sqlalchemy-2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-sqlalchemy-2 -- -ra + + pypy3-test-instrumentation-sqlalchemy-1_ubuntu-latest: + name: instrumentation-sqlalchemy-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-sqlalchemy-1 -- -ra + + pypy3-test-instrumentation-sqlalchemy-2_ubuntu-latest: + name: instrumentation-sqlalchemy-2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-sqlalchemy-2 -- -ra + + py310-test-instrumentation-redis_ubuntu-latest: + name: instrumentation-redis 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-redis -- -ra + + py311-test-instrumentation-redis_ubuntu-latest: + name: instrumentation-redis 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-redis -- -ra + + py312-test-instrumentation-redis_ubuntu-latest: + name: instrumentation-redis 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-redis -- -ra + + py313-test-instrumentation-redis_ubuntu-latest: + name: instrumentation-redis 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-redis -- -ra + + py314-test-instrumentation-redis_ubuntu-latest: + name: instrumentation-redis 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-redis -- -ra + + pypy3-test-instrumentation-redis_ubuntu-latest: + name: instrumentation-redis pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-redis -- -ra + + py310-test-instrumentation-remoulade_ubuntu-latest: + name: instrumentation-remoulade 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-remoulade -- -ra + + py311-test-instrumentation-remoulade_ubuntu-latest: + name: instrumentation-remoulade 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-remoulade -- -ra + + py312-test-instrumentation-remoulade_ubuntu-latest: + name: instrumentation-remoulade 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-remoulade -- -ra + + py313-test-instrumentation-remoulade_ubuntu-latest: + name: instrumentation-remoulade 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-remoulade -- -ra + + py314-test-instrumentation-remoulade_ubuntu-latest: + name: instrumentation-remoulade 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-remoulade -- -ra + + py310-test-instrumentation-celery_ubuntu-latest: + name: instrumentation-celery 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-celery -- -ra + + py311-test-instrumentation-celery_ubuntu-latest: + name: instrumentation-celery 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-celery -- -ra + + py312-test-instrumentation-celery_ubuntu-latest: + name: instrumentation-celery 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-celery -- -ra + + py313-test-instrumentation-celery_ubuntu-latest: + name: instrumentation-celery 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-celery -- -ra + + py314-test-instrumentation-celery_ubuntu-latest: + name: instrumentation-celery 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-celery -- -ra + + pypy3-test-instrumentation-celery_ubuntu-latest: + name: instrumentation-celery pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-celery -- -ra + + py310-test-instrumentation-system-metrics_ubuntu-latest: + name: instrumentation-system-metrics 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-system-metrics -- -ra + + py311-test-instrumentation-system-metrics_ubuntu-latest: + name: instrumentation-system-metrics 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-system-metrics -- -ra + + py312-test-instrumentation-system-metrics_ubuntu-latest: + name: instrumentation-system-metrics 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-system-metrics -- -ra + + py313-test-instrumentation-system-metrics_ubuntu-latest: + name: instrumentation-system-metrics 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-system-metrics -- -ra + + py314-test-instrumentation-system-metrics_ubuntu-latest: + name: instrumentation-system-metrics 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-system-metrics -- -ra + + pypy3-test-instrumentation-system-metrics_ubuntu-latest: + name: instrumentation-system-metrics pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-system-metrics -- -ra + + py310-test-instrumentation-threading_ubuntu-latest: + name: instrumentation-threading 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-threading -- -ra + + py311-test-instrumentation-threading_ubuntu-latest: + name: instrumentation-threading 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-threading -- -ra + + py312-test-instrumentation-threading_ubuntu-latest: + name: instrumentation-threading 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-threading -- -ra + + py313-test-instrumentation-threading_ubuntu-latest: + name: instrumentation-threading 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-threading -- -ra + + py314-test-instrumentation-threading_ubuntu-latest: + name: instrumentation-threading 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-threading -- -ra + + pypy3-test-instrumentation-threading_ubuntu-latest: + name: instrumentation-threading pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-threading -- -ra + + py310-test-instrumentation-tornado_ubuntu-latest: + name: instrumentation-tornado 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-tornado -- -ra + + py311-test-instrumentation-tornado_ubuntu-latest: + name: instrumentation-tornado 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-tornado -- -ra + + py312-test-instrumentation-tornado_ubuntu-latest: + name: instrumentation-tornado 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-tornado -- -ra + + py313-test-instrumentation-tornado_ubuntu-latest: + name: instrumentation-tornado 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-tornado -- -ra + + py314-test-instrumentation-tornado_ubuntu-latest: + name: instrumentation-tornado 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-tornado -- -ra + + pypy3-test-instrumentation-tornado_ubuntu-latest: + name: instrumentation-tornado pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-tornado -- -ra + + py310-test-instrumentation-tortoiseorm_ubuntu-latest: + name: instrumentation-tortoiseorm 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-tortoiseorm -- -ra + + py311-test-instrumentation-tortoiseorm_ubuntu-latest: + name: instrumentation-tortoiseorm 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-tortoiseorm -- -ra + + py312-test-instrumentation-tortoiseorm_ubuntu-latest: + name: instrumentation-tortoiseorm 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-tortoiseorm -- -ra + + py313-test-instrumentation-tortoiseorm_ubuntu-latest: + name: instrumentation-tortoiseorm 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-tortoiseorm -- -ra + + py314-test-instrumentation-tortoiseorm_ubuntu-latest: + name: instrumentation-tortoiseorm 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-tortoiseorm -- -ra + + pypy3-test-instrumentation-tortoiseorm_ubuntu-latest: + name: instrumentation-tortoiseorm pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-tortoiseorm -- -ra + + py310-test-instrumentation-httpx-0-wrapt1_ubuntu-latest: + name: instrumentation-httpx-0-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-httpx-0-wrapt1 -- -ra + + py310-test-instrumentation-httpx-0-wrapt2_ubuntu-latest: + name: instrumentation-httpx-0-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-httpx-0-wrapt2 -- -ra + + py310-test-instrumentation-httpx-1-wrapt1_ubuntu-latest: + name: instrumentation-httpx-1-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-httpx-1-wrapt1 -- -ra + + py310-test-instrumentation-httpx-1-wrapt2_ubuntu-latest: + name: instrumentation-httpx-1-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-httpx-1-wrapt2 -- -ra + + py311-test-instrumentation-httpx-0-wrapt1_ubuntu-latest: + name: instrumentation-httpx-0-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-httpx-0-wrapt1 -- -ra + + py311-test-instrumentation-httpx-0-wrapt2_ubuntu-latest: + name: instrumentation-httpx-0-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-httpx-0-wrapt2 -- -ra + + py311-test-instrumentation-httpx-1-wrapt1_ubuntu-latest: + name: instrumentation-httpx-1-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-httpx-1-wrapt1 -- -ra + + py311-test-instrumentation-httpx-1-wrapt2_ubuntu-latest: + name: instrumentation-httpx-1-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-httpx-1-wrapt2 -- -ra + + py312-test-instrumentation-httpx-0-wrapt1_ubuntu-latest: + name: instrumentation-httpx-0-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-httpx-0-wrapt1 -- -ra + + py312-test-instrumentation-httpx-0-wrapt2_ubuntu-latest: + name: instrumentation-httpx-0-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-httpx-0-wrapt2 -- -ra + + py312-test-instrumentation-httpx-1-wrapt1_ubuntu-latest: + name: instrumentation-httpx-1-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-httpx-1-wrapt1 -- -ra + + py312-test-instrumentation-httpx-1-wrapt2_ubuntu-latest: + name: instrumentation-httpx-1-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-httpx-1-wrapt2 -- -ra + + py313-test-instrumentation-httpx-1-wrapt1_ubuntu-latest: + name: instrumentation-httpx-1-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-httpx-1-wrapt1 -- -ra + + py313-test-instrumentation-httpx-1-wrapt2_ubuntu-latest: + name: instrumentation-httpx-1-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-httpx-1-wrapt2 -- -ra + + py314-test-instrumentation-httpx-1-wrapt1_ubuntu-latest: + name: instrumentation-httpx-1-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-httpx-1-wrapt1 -- -ra + + py314-test-instrumentation-httpx-1-wrapt2_ubuntu-latest: + name: instrumentation-httpx-1-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-httpx-1-wrapt2 -- -ra + + pypy3-test-instrumentation-httpx-0-wrapt2_ubuntu-latest: + name: instrumentation-httpx-0-wrapt2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-httpx-0-wrapt2 -- -ra + + pypy3-test-instrumentation-httpx-1-wrapt2_ubuntu-latest: + name: instrumentation-httpx-1-wrapt2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-httpx-1-wrapt2 -- -ra + + py310-test-util-http_ubuntu-latest: + name: util-http 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-util-http -- -ra + + py311-test-util-http_ubuntu-latest: + name: util-http 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-util-http -- -ra + + py312-test-util-http_ubuntu-latest: + name: util-http 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-util-http -- -ra + + py313-test-util-http_ubuntu-latest: + name: util-http 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-util-http -- -ra + + py314-test-util-http_ubuntu-latest: + name: util-http 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-util-http -- -ra + + pypy3-test-util-http_ubuntu-latest: + name: util-http pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-util-http -- -ra + + py310-test-exporter-credential-provider-gcp_ubuntu-latest: + name: exporter-credential-provider-gcp 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-exporter-credential-provider-gcp -- -ra + + py311-test-exporter-credential-provider-gcp_ubuntu-latest: + name: exporter-credential-provider-gcp 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-exporter-credential-provider-gcp -- -ra + + py312-test-exporter-credential-provider-gcp_ubuntu-latest: + name: exporter-credential-provider-gcp 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-exporter-credential-provider-gcp -- -ra + + py313-test-exporter-credential-provider-gcp_ubuntu-latest: + name: exporter-credential-provider-gcp 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-exporter-credential-provider-gcp -- -ra + + py314-test-exporter-credential-provider-gcp_ubuntu-latest: + name: exporter-credential-provider-gcp 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-exporter-credential-provider-gcp -- -ra + + py310-test-util-genai_ubuntu-latest: + name: util-genai 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-util-genai -- -ra + + py311-test-util-genai_ubuntu-latest: + name: util-genai 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-util-genai -- -ra + + py312-test-util-genai_ubuntu-latest: + name: util-genai 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-util-genai -- -ra + + py313-test-util-genai_ubuntu-latest: + name: util-genai 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-util-genai -- -ra + + py314-test-util-genai_ubuntu-latest: + name: util-genai 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-util-genai -- -ra + + pypy3-test-util-genai_ubuntu-latest: + name: util-genai pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-util-genai -- -ra + + py310-test-propagator-aws-xray-0_ubuntu-latest: + name: propagator-aws-xray-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-propagator-aws-xray-0 -- -ra + + py310-test-propagator-aws-xray-1_ubuntu-latest: + name: propagator-aws-xray-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-propagator-aws-xray-1 -- -ra + + py311-test-propagator-aws-xray-0_ubuntu-latest: + name: propagator-aws-xray-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-propagator-aws-xray-0 -- -ra + + py311-test-propagator-aws-xray-1_ubuntu-latest: + name: propagator-aws-xray-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-propagator-aws-xray-1 -- -ra + + py312-test-propagator-aws-xray-0_ubuntu-latest: + name: propagator-aws-xray-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-propagator-aws-xray-0 -- -ra + + py312-test-propagator-aws-xray-1_ubuntu-latest: + name: propagator-aws-xray-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-propagator-aws-xray-1 -- -ra + + py313-test-propagator-aws-xray-0_ubuntu-latest: + name: propagator-aws-xray-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-propagator-aws-xray-0 -- -ra + + py313-test-propagator-aws-xray-1_ubuntu-latest: + name: propagator-aws-xray-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-propagator-aws-xray-1 -- -ra + + py314-test-propagator-aws-xray-0_ubuntu-latest: + name: propagator-aws-xray-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-propagator-aws-xray-0 -- -ra + + py314-test-propagator-aws-xray-1_ubuntu-latest: + name: propagator-aws-xray-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-propagator-aws-xray-1 -- -ra + + pypy3-test-propagator-aws-xray-0_ubuntu-latest: + name: propagator-aws-xray-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-propagator-aws-xray-0 -- -ra + + pypy3-test-propagator-aws-xray-1_ubuntu-latest: + name: propagator-aws-xray-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-propagator-aws-xray-1 -- -ra + + py310-test-propagator-ot-trace_ubuntu-latest: + name: propagator-ot-trace 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-propagator-ot-trace -- -ra + + py311-test-propagator-ot-trace_ubuntu-latest: + name: propagator-ot-trace 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-propagator-ot-trace -- -ra + + py312-test-propagator-ot-trace_ubuntu-latest: + name: propagator-ot-trace 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-propagator-ot-trace -- -ra + + py313-test-propagator-ot-trace_ubuntu-latest: + name: propagator-ot-trace 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-propagator-ot-trace -- -ra + + py314-test-propagator-ot-trace_ubuntu-latest: + name: propagator-ot-trace 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-propagator-ot-trace -- -ra + + pypy3-test-propagator-ot-trace_ubuntu-latest: + name: propagator-ot-trace pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-propagator-ot-trace -- -ra + + py310-test-instrumentation-sio-pika-0-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sio-pika-0-wrapt1 -- -ra + + py310-test-instrumentation-sio-pika-0-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sio-pika-0-wrapt2 -- -ra + + py310-test-instrumentation-sio-pika-1-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sio-pika-1-wrapt1 -- -ra + + py310-test-instrumentation-sio-pika-1-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-sio-pika-1-wrapt2 -- -ra + + py311-test-instrumentation-sio-pika-0-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-sio-pika-0-wrapt1 -- -ra + + py311-test-instrumentation-sio-pika-0-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-sio-pika-0-wrapt2 -- -ra + + py311-test-instrumentation-sio-pika-1-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-sio-pika-1-wrapt1 -- -ra + + py311-test-instrumentation-sio-pika-1-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-sio-pika-1-wrapt2 -- -ra + + py312-test-instrumentation-sio-pika-0-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-sio-pika-0-wrapt1 -- -ra + + py312-test-instrumentation-sio-pika-0-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-sio-pika-0-wrapt2 -- -ra + + py312-test-instrumentation-sio-pika-1-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-sio-pika-1-wrapt1 -- -ra + + py312-test-instrumentation-sio-pika-1-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-sio-pika-1-wrapt2 -- -ra + + py313-test-instrumentation-sio-pika-0-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-sio-pika-0-wrapt1 -- -ra + + py313-test-instrumentation-sio-pika-0-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-sio-pika-0-wrapt2 -- -ra + + py313-test-instrumentation-sio-pika-1-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-sio-pika-1-wrapt1 -- -ra + + py313-test-instrumentation-sio-pika-1-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-sio-pika-1-wrapt2 -- -ra + + py314-test-instrumentation-sio-pika-0-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-sio-pika-0-wrapt1 -- -ra + + py314-test-instrumentation-sio-pika-0-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-sio-pika-0-wrapt2 -- -ra + + py314-test-instrumentation-sio-pika-1-wrapt1_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-sio-pika-1-wrapt1 -- -ra + + py314-test-instrumentation-sio-pika-1-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-sio-pika-1-wrapt2 -- -ra + + pypy3-test-instrumentation-sio-pika-0-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-0-wrapt2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-sio-pika-0-wrapt2 -- -ra + + pypy3-test-instrumentation-sio-pika-1-wrapt2_ubuntu-latest: + name: instrumentation-sio-pika-1-wrapt2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-sio-pika-1-wrapt2 -- -ra + + py310-test-instrumentation-aio-pika-0_ubuntu-latest: + name: instrumentation-aio-pika-0 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aio-pika-0 -- -ra + + py310-test-instrumentation-aio-pika-1_ubuntu-latest: + name: instrumentation-aio-pika-1 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aio-pika-1 -- -ra + + py310-test-instrumentation-aio-pika-2_ubuntu-latest: + name: instrumentation-aio-pika-2 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aio-pika-2 -- -ra + + py310-test-instrumentation-aio-pika-3_ubuntu-latest: + name: instrumentation-aio-pika-3 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aio-pika-3 -- -ra + + py311-test-instrumentation-aio-pika-0_ubuntu-latest: + name: instrumentation-aio-pika-0 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aio-pika-0 -- -ra + + py311-test-instrumentation-aio-pika-1_ubuntu-latest: + name: instrumentation-aio-pika-1 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aio-pika-1 -- -ra + + py311-test-instrumentation-aio-pika-2_ubuntu-latest: + name: instrumentation-aio-pika-2 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aio-pika-2 -- -ra + + py311-test-instrumentation-aio-pika-3_ubuntu-latest: + name: instrumentation-aio-pika-3 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aio-pika-3 -- -ra + + py312-test-instrumentation-aio-pika-0_ubuntu-latest: + name: instrumentation-aio-pika-0 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aio-pika-0 -- -ra + + py312-test-instrumentation-aio-pika-1_ubuntu-latest: + name: instrumentation-aio-pika-1 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aio-pika-1 -- -ra + + py312-test-instrumentation-aio-pika-2_ubuntu-latest: + name: instrumentation-aio-pika-2 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aio-pika-2 -- -ra + + py312-test-instrumentation-aio-pika-3_ubuntu-latest: + name: instrumentation-aio-pika-3 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aio-pika-3 -- -ra + + py313-test-instrumentation-aio-pika-0_ubuntu-latest: + name: instrumentation-aio-pika-0 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aio-pika-0 -- -ra + + py313-test-instrumentation-aio-pika-1_ubuntu-latest: + name: instrumentation-aio-pika-1 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aio-pika-1 -- -ra + + py313-test-instrumentation-aio-pika-2_ubuntu-latest: + name: instrumentation-aio-pika-2 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aio-pika-2 -- -ra + + py313-test-instrumentation-aio-pika-3_ubuntu-latest: + name: instrumentation-aio-pika-3 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aio-pika-3 -- -ra + + py314-test-instrumentation-aio-pika-0_ubuntu-latest: + name: instrumentation-aio-pika-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aio-pika-0 -- -ra + + py314-test-instrumentation-aio-pika-1_ubuntu-latest: + name: instrumentation-aio-pika-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aio-pika-1 -- -ra + + py314-test-instrumentation-aio-pika-2_ubuntu-latest: + name: instrumentation-aio-pika-2 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aio-pika-2 -- -ra + + py314-test-instrumentation-aio-pika-3_ubuntu-latest: + name: instrumentation-aio-pika-3 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aio-pika-3 -- -ra + + pypy3-test-instrumentation-aio-pika-0_ubuntu-latest: + name: instrumentation-aio-pika-0 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aio-pika-0 -- -ra + + pypy3-test-instrumentation-aio-pika-1_ubuntu-latest: + name: instrumentation-aio-pika-1 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aio-pika-1 -- -ra + + pypy3-test-instrumentation-aio-pika-2_ubuntu-latest: + name: instrumentation-aio-pika-2 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aio-pika-2 -- -ra + + pypy3-test-instrumentation-aio-pika-3_ubuntu-latest: + name: instrumentation-aio-pika-3 pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aio-pika-3 -- -ra + + py310-test-instrumentation-aiokafka_ubuntu-latest: + name: instrumentation-aiokafka 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-aiokafka -- -ra + + py311-test-instrumentation-aiokafka_ubuntu-latest: + name: instrumentation-aiokafka 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-aiokafka -- -ra + + py312-test-instrumentation-aiokafka_ubuntu-latest: + name: instrumentation-aiokafka 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-aiokafka -- -ra + + py313-test-instrumentation-aiokafka_ubuntu-latest: + name: instrumentation-aiokafka 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-aiokafka -- -ra + + py314-test-instrumentation-aiokafka_ubuntu-latest: + name: instrumentation-aiokafka 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-aiokafka -- -ra + + pypy3-test-instrumentation-aiokafka_ubuntu-latest: + name: instrumentation-aiokafka pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-aiokafka -- -ra + + py310-test-instrumentation-kafka-python_ubuntu-latest: + name: instrumentation-kafka-python 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-kafka-python -- -ra + + py311-test-instrumentation-kafka-python_ubuntu-latest: + name: instrumentation-kafka-python 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-kafka-python -- -ra + + py310-test-instrumentation-kafka-pythonng_ubuntu-latest: + name: instrumentation-kafka-pythonng 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-kafka-pythonng -- -ra + + py311-test-instrumentation-kafka-pythonng_ubuntu-latest: + name: instrumentation-kafka-pythonng 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-kafka-pythonng -- -ra + + py312-test-instrumentation-kafka-pythonng_ubuntu-latest: + name: instrumentation-kafka-pythonng 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-kafka-pythonng -- -ra + + py313-test-instrumentation-kafka-pythonng_ubuntu-latest: + name: instrumentation-kafka-pythonng 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-kafka-pythonng -- -ra + + py314-test-instrumentation-kafka-pythonng_ubuntu-latest: + name: instrumentation-kafka-pythonng 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-kafka-pythonng -- -ra + + pypy3-test-instrumentation-kafka-python_ubuntu-latest: + name: instrumentation-kafka-python pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-kafka-python -- -ra + + pypy3-test-instrumentation-kafka-pythonng_ubuntu-latest: + name: instrumentation-kafka-pythonng pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-kafka-pythonng -- -ra + + py310-test-instrumentation-confluent-kafka_ubuntu-latest: + name: instrumentation-confluent-kafka 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-confluent-kafka -- -ra + + py311-test-instrumentation-confluent-kafka_ubuntu-latest: + name: instrumentation-confluent-kafka 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-confluent-kafka -- -ra + + py312-test-instrumentation-confluent-kafka_ubuntu-latest: + name: instrumentation-confluent-kafka 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-confluent-kafka -- -ra + + py313-test-instrumentation-confluent-kafka_ubuntu-latest: + name: instrumentation-confluent-kafka 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-confluent-kafka -- -ra + + py314-test-instrumentation-confluent-kafka_ubuntu-latest: + name: instrumentation-confluent-kafka 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-confluent-kafka -- -ra + + py310-test-instrumentation-asyncio_ubuntu-latest: + name: instrumentation-asyncio 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-asyncio -- -ra + + py311-test-instrumentation-asyncio_ubuntu-latest: + name: instrumentation-asyncio 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-asyncio -- -ra + + py312-test-instrumentation-asyncio_ubuntu-latest: + name: instrumentation-asyncio 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-asyncio -- -ra + + py313-test-instrumentation-asyncio_ubuntu-latest: + name: instrumentation-asyncio 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-asyncio -- -ra + + py314-test-instrumentation-asyncio_ubuntu-latest: + name: instrumentation-asyncio 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-asyncio -- -ra + + py310-test-instrumentation-cassandra-driver_ubuntu-latest: + name: instrumentation-cassandra-driver 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-cassandra-driver -- -ra + + py310-test-instrumentation-cassandra-scylla_ubuntu-latest: + name: instrumentation-cassandra-scylla 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-instrumentation-cassandra-scylla -- -ra + + py311-test-instrumentation-cassandra-driver_ubuntu-latest: + name: instrumentation-cassandra-driver 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-cassandra-driver -- -ra + + py311-test-instrumentation-cassandra-scylla_ubuntu-latest: + name: instrumentation-cassandra-scylla 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-instrumentation-cassandra-scylla -- -ra + + py312-test-instrumentation-cassandra-driver_ubuntu-latest: + name: instrumentation-cassandra-driver 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-cassandra-driver -- -ra + + py312-test-instrumentation-cassandra-scylla_ubuntu-latest: + name: instrumentation-cassandra-scylla 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-instrumentation-cassandra-scylla -- -ra + + py313-test-instrumentation-cassandra-driver_ubuntu-latest: + name: instrumentation-cassandra-driver 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-cassandra-driver -- -ra + + py313-test-instrumentation-cassandra-scylla_ubuntu-latest: + name: instrumentation-cassandra-scylla 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-instrumentation-cassandra-scylla -- -ra + + py314-test-instrumentation-cassandra-driver_ubuntu-latest: + name: instrumentation-cassandra-driver 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-cassandra-driver -- -ra + + py314-test-instrumentation-cassandra-scylla_ubuntu-latest: + name: instrumentation-cassandra-scylla 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-cassandra-scylla -- -ra + + pypy3-test-instrumentation-cassandra-scylla_ubuntu-latest: + name: instrumentation-cassandra-scylla pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-instrumentation-cassandra-scylla -- -ra + + py310-test-processor-baggage_ubuntu-latest: + name: processor-baggage 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-processor-baggage -- -ra + + py311-test-processor-baggage_ubuntu-latest: + name: processor-baggage 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-processor-baggage -- -ra + + py312-test-processor-baggage_ubuntu-latest: + name: processor-baggage 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-processor-baggage -- -ra + + py313-test-processor-baggage_ubuntu-latest: + name: processor-baggage 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-processor-baggage -- -ra + + py314-test-processor-baggage_ubuntu-latest: + name: processor-baggage 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-processor-baggage -- -ra + + pypy3-test-processor-baggage_ubuntu-latest: + name: processor-baggage pypy-3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python pypy-3.10 + uses: actions/setup-python@v5 + with: + python-version: "pypy-3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e pypy3-test-processor-baggage -- -ra + + py310-test-opamp-client-latest_ubuntu-latest: + name: opamp-client-latest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-opamp-client-latest -- -ra + + py310-test-opamp-client-lowest_ubuntu-latest: + name: opamp-client-lowest 3.10 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py310-test-opamp-client-lowest -- -ra + + py311-test-opamp-client-latest_ubuntu-latest: + name: opamp-client-latest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-opamp-client-latest -- -ra + + py311-test-opamp-client-lowest_ubuntu-latest: + name: opamp-client-lowest 3.11 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py311-test-opamp-client-lowest -- -ra + + py312-test-opamp-client-latest_ubuntu-latest: + name: opamp-client-latest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-opamp-client-latest -- -ra + + py312-test-opamp-client-lowest_ubuntu-latest: + name: opamp-client-lowest 3.12 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py312-test-opamp-client-lowest -- -ra + + py313-test-opamp-client-latest_ubuntu-latest: + name: opamp-client-latest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-opamp-client-latest -- -ra + + py313-test-opamp-client-lowest_ubuntu-latest: + name: opamp-client-lowest 3.13 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py313-test-opamp-client-lowest -- -ra + + py314-test-opamp-client-latest_ubuntu-latest: + name: opamp-client-latest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-opamp-client-latest -- -ra + + py314-test-opamp-client-lowest_ubuntu-latest: + name: opamp-client-lowest 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-opamp-client-lowest -- -ra diff --git a/.github/workflows/test_0.yml b/.github/workflows/test_0.yml deleted file mode 100644 index f057dbc7b9..0000000000 --- a/.github/workflows/test_0.yml +++ /dev/null @@ -1,4783 +0,0 @@ -# Do not edit this file. -# This file is generated automatically by executing tox -e generate-workflows - -name: Test 0 - -on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -env: - # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' - # For PRs you can change the inner fallback ('main') - # For pushes you change the outer fallback ('main') - # The logic below is used during releases and depends on having an equivalent branch name in the core repo. - CORE_REPO_SHA: ${{ github.event_name == 'pull_request' && ( - contains(github.event.pull_request.labels.*.name, 'prepare-release') && github.event.pull_request.head.ref || - contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref || - 'main' - ) || 'main' }} - CONTRIB_REPO_SHA: main - PIP_EXISTS_ACTION: w - -jobs: - - py39-test-instrumentation-openai-v2-oldest_ubuntu-latest: - name: instrumentation-openai-v2-oldest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-openai-v2-oldest -- -ra - - py39-test-instrumentation-openai-v2-latest_ubuntu-latest: - name: instrumentation-openai-v2-latest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-openai-v2-latest -- -ra - - py310-test-instrumentation-openai-v2-oldest_ubuntu-latest: - name: instrumentation-openai-v2-oldest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-openai-v2-oldest -- -ra - - py310-test-instrumentation-openai-v2-latest_ubuntu-latest: - name: instrumentation-openai-v2-latest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-openai-v2-latest -- -ra - - py311-test-instrumentation-openai-v2-oldest_ubuntu-latest: - name: instrumentation-openai-v2-oldest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-openai-v2-oldest -- -ra - - py311-test-instrumentation-openai-v2-latest_ubuntu-latest: - name: instrumentation-openai-v2-latest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-openai-v2-latest -- -ra - - py312-test-instrumentation-openai-v2-oldest_ubuntu-latest: - name: instrumentation-openai-v2-oldest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-openai-v2-oldest -- -ra - - py312-test-instrumentation-openai-v2-latest_ubuntu-latest: - name: instrumentation-openai-v2-latest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-openai-v2-latest -- -ra - - py313-test-instrumentation-openai-v2-oldest_ubuntu-latest: - name: instrumentation-openai-v2-oldest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-openai-v2-oldest -- -ra - - py313-test-instrumentation-openai-v2-latest_ubuntu-latest: - name: instrumentation-openai-v2-latest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-openai-v2-latest -- -ra - - py314-test-instrumentation-openai-v2-oldest_ubuntu-latest: - name: instrumentation-openai-v2-oldest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-openai-v2-oldest -- -ra - - py314-test-instrumentation-openai-v2-latest_ubuntu-latest: - name: instrumentation-openai-v2-latest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-openai-v2-latest -- -ra - - pypy3-test-instrumentation-openai-v2-oldest_ubuntu-latest: - name: instrumentation-openai-v2-oldest pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-openai-v2-oldest -- -ra - - pypy3-test-instrumentation-openai-v2-latest_ubuntu-latest: - name: instrumentation-openai-v2-latest pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-openai-v2-latest -- -ra - - py310-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: - name: instrumentation-openai_agents-v2-oldest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-openai_agents-v2-oldest -- -ra - - py310-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: - name: instrumentation-openai_agents-v2-latest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-openai_agents-v2-latest -- -ra - - py311-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: - name: instrumentation-openai_agents-v2-oldest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-openai_agents-v2-oldest -- -ra - - py311-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: - name: instrumentation-openai_agents-v2-latest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-openai_agents-v2-latest -- -ra - - py312-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: - name: instrumentation-openai_agents-v2-oldest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-openai_agents-v2-oldest -- -ra - - py312-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: - name: instrumentation-openai_agents-v2-latest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-openai_agents-v2-latest -- -ra - - py313-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: - name: instrumentation-openai_agents-v2-oldest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-openai_agents-v2-oldest -- -ra - - py313-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: - name: instrumentation-openai_agents-v2-latest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-openai_agents-v2-latest -- -ra - - py314-test-instrumentation-openai_agents-v2-oldest_ubuntu-latest: - name: instrumentation-openai_agents-v2-oldest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-openai_agents-v2-oldest -- -ra - - py314-test-instrumentation-openai_agents-v2-latest_ubuntu-latest: - name: instrumentation-openai_agents-v2-latest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-openai_agents-v2-latest -- -ra - - py39-test-instrumentation-vertexai-oldest_ubuntu-latest: - name: instrumentation-vertexai-oldest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-vertexai-oldest -- -ra - - py39-test-instrumentation-vertexai-latest_ubuntu-latest: - name: instrumentation-vertexai-latest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-vertexai-latest -- -ra - - py310-test-instrumentation-vertexai-oldest_ubuntu-latest: - name: instrumentation-vertexai-oldest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-vertexai-oldest -- -ra - - py310-test-instrumentation-vertexai-latest_ubuntu-latest: - name: instrumentation-vertexai-latest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-vertexai-latest -- -ra - - py311-test-instrumentation-vertexai-oldest_ubuntu-latest: - name: instrumentation-vertexai-oldest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-vertexai-oldest -- -ra - - py311-test-instrumentation-vertexai-latest_ubuntu-latest: - name: instrumentation-vertexai-latest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-vertexai-latest -- -ra - - py312-test-instrumentation-vertexai-oldest_ubuntu-latest: - name: instrumentation-vertexai-oldest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-vertexai-oldest -- -ra - - py312-test-instrumentation-vertexai-latest_ubuntu-latest: - name: instrumentation-vertexai-latest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-vertexai-latest -- -ra - - py313-test-instrumentation-vertexai-oldest_ubuntu-latest: - name: instrumentation-vertexai-oldest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-vertexai-oldest -- -ra - - py313-test-instrumentation-vertexai-latest_ubuntu-latest: - name: instrumentation-vertexai-latest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-vertexai-latest -- -ra - - py314-test-instrumentation-vertexai-oldest_ubuntu-latest: - name: instrumentation-vertexai-oldest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-vertexai-oldest -- -ra - - py314-test-instrumentation-vertexai-latest_ubuntu-latest: - name: instrumentation-vertexai-latest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-vertexai-latest -- -ra - - py39-test-instrumentation-google-genai-oldest_ubuntu-latest: - name: instrumentation-google-genai-oldest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-google-genai-oldest -- -ra - - py39-test-instrumentation-google-genai-latest_ubuntu-latest: - name: instrumentation-google-genai-latest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-google-genai-latest -- -ra - - py310-test-instrumentation-google-genai-oldest_ubuntu-latest: - name: instrumentation-google-genai-oldest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-google-genai-oldest -- -ra - - py310-test-instrumentation-google-genai-latest_ubuntu-latest: - name: instrumentation-google-genai-latest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-google-genai-latest -- -ra - - py311-test-instrumentation-google-genai-oldest_ubuntu-latest: - name: instrumentation-google-genai-oldest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-google-genai-oldest -- -ra - - py311-test-instrumentation-google-genai-latest_ubuntu-latest: - name: instrumentation-google-genai-latest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-google-genai-latest -- -ra - - py312-test-instrumentation-google-genai-oldest_ubuntu-latest: - name: instrumentation-google-genai-oldest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-google-genai-oldest -- -ra - - py312-test-instrumentation-google-genai-latest_ubuntu-latest: - name: instrumentation-google-genai-latest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-google-genai-latest -- -ra - - py313-test-instrumentation-google-genai-oldest_ubuntu-latest: - name: instrumentation-google-genai-oldest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-google-genai-oldest -- -ra - - py313-test-instrumentation-google-genai-latest_ubuntu-latest: - name: instrumentation-google-genai-latest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-google-genai-latest -- -ra - - py314-test-instrumentation-google-genai-oldest_ubuntu-latest: - name: instrumentation-google-genai-oldest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-google-genai-oldest -- -ra - - py314-test-instrumentation-google-genai-latest_ubuntu-latest: - name: instrumentation-google-genai-latest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-google-genai-latest -- -ra - - py39-test-instrumentation-anthropic-oldest_ubuntu-latest: - name: instrumentation-anthropic-oldest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-anthropic-oldest -- -ra - - py39-test-instrumentation-anthropic-latest_ubuntu-latest: - name: instrumentation-anthropic-latest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-anthropic-latest -- -ra - - py310-test-instrumentation-anthropic-oldest_ubuntu-latest: - name: instrumentation-anthropic-oldest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-anthropic-oldest -- -ra - - py310-test-instrumentation-anthropic-latest_ubuntu-latest: - name: instrumentation-anthropic-latest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-anthropic-latest -- -ra - - py311-test-instrumentation-anthropic-oldest_ubuntu-latest: - name: instrumentation-anthropic-oldest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-anthropic-oldest -- -ra - - py311-test-instrumentation-anthropic-latest_ubuntu-latest: - name: instrumentation-anthropic-latest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-anthropic-latest -- -ra - - py312-test-instrumentation-anthropic-oldest_ubuntu-latest: - name: instrumentation-anthropic-oldest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-anthropic-oldest -- -ra - - py312-test-instrumentation-anthropic-latest_ubuntu-latest: - name: instrumentation-anthropic-latest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-anthropic-latest -- -ra - - py313-test-instrumentation-anthropic-oldest_ubuntu-latest: - name: instrumentation-anthropic-oldest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-anthropic-oldest -- -ra - - py313-test-instrumentation-anthropic-latest_ubuntu-latest: - name: instrumentation-anthropic-latest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-anthropic-latest -- -ra - - py314-test-instrumentation-anthropic-oldest_ubuntu-latest: - name: instrumentation-anthropic-oldest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-anthropic-oldest -- -ra - - py314-test-instrumentation-anthropic-latest_ubuntu-latest: - name: instrumentation-anthropic-latest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-anthropic-latest -- -ra - - py310-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-oldest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-claude-agent-sdk-oldest -- -ra - - py310-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-latest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-claude-agent-sdk-latest -- -ra - - py311-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-oldest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-claude-agent-sdk-oldest -- -ra - - py311-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-latest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-claude-agent-sdk-latest -- -ra - - py312-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-oldest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-claude-agent-sdk-oldest -- -ra - - py312-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-latest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-claude-agent-sdk-latest -- -ra - - py313-test-instrumentation-claude-agent-sdk-oldest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-oldest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-claude-agent-sdk-oldest -- -ra - - py313-test-instrumentation-claude-agent-sdk-latest_ubuntu-latest: - name: instrumentation-claude-agent-sdk-latest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-claude-agent-sdk-latest -- -ra - - py39-test-resource-detector-containerid_ubuntu-latest: - name: resource-detector-containerid 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-resource-detector-containerid -- -ra - - py310-test-resource-detector-containerid_ubuntu-latest: - name: resource-detector-containerid 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-resource-detector-containerid -- -ra - - py311-test-resource-detector-containerid_ubuntu-latest: - name: resource-detector-containerid 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-resource-detector-containerid -- -ra - - py312-test-resource-detector-containerid_ubuntu-latest: - name: resource-detector-containerid 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-resource-detector-containerid -- -ra - - py313-test-resource-detector-containerid_ubuntu-latest: - name: resource-detector-containerid 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-resource-detector-containerid -- -ra - - py314-test-resource-detector-containerid_ubuntu-latest: - name: resource-detector-containerid 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-resource-detector-containerid -- -ra - - pypy3-test-resource-detector-containerid_ubuntu-latest: - name: resource-detector-containerid pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-resource-detector-containerid -- -ra - - py39-test-resource-detector-azure-0_ubuntu-latest: - name: resource-detector-azure-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-resource-detector-azure-0 -- -ra - - py39-test-resource-detector-azure-1_ubuntu-latest: - name: resource-detector-azure-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-resource-detector-azure-1 -- -ra - - py310-test-resource-detector-azure-0_ubuntu-latest: - name: resource-detector-azure-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-resource-detector-azure-0 -- -ra - - py310-test-resource-detector-azure-1_ubuntu-latest: - name: resource-detector-azure-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-resource-detector-azure-1 -- -ra - - py311-test-resource-detector-azure-0_ubuntu-latest: - name: resource-detector-azure-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-resource-detector-azure-0 -- -ra - - py311-test-resource-detector-azure-1_ubuntu-latest: - name: resource-detector-azure-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-resource-detector-azure-1 -- -ra - - py312-test-resource-detector-azure-0_ubuntu-latest: - name: resource-detector-azure-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-resource-detector-azure-0 -- -ra - - py312-test-resource-detector-azure-1_ubuntu-latest: - name: resource-detector-azure-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-resource-detector-azure-1 -- -ra - - py313-test-resource-detector-azure-0_ubuntu-latest: - name: resource-detector-azure-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-resource-detector-azure-0 -- -ra - - py313-test-resource-detector-azure-1_ubuntu-latest: - name: resource-detector-azure-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-resource-detector-azure-1 -- -ra - - py314-test-resource-detector-azure-0_ubuntu-latest: - name: resource-detector-azure-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-resource-detector-azure-0 -- -ra - - py314-test-resource-detector-azure-1_ubuntu-latest: - name: resource-detector-azure-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-resource-detector-azure-1 -- -ra - - pypy3-test-resource-detector-azure-0_ubuntu-latest: - name: resource-detector-azure-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-resource-detector-azure-0 -- -ra - - pypy3-test-resource-detector-azure-1_ubuntu-latest: - name: resource-detector-azure-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-resource-detector-azure-1 -- -ra - - py39-test-sdk-extension-aws-0_ubuntu-latest: - name: sdk-extension-aws-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-sdk-extension-aws-0 -- -ra - - py39-test-sdk-extension-aws-1_ubuntu-latest: - name: sdk-extension-aws-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-sdk-extension-aws-1 -- -ra - - py310-test-sdk-extension-aws-0_ubuntu-latest: - name: sdk-extension-aws-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-sdk-extension-aws-0 -- -ra - - py310-test-sdk-extension-aws-1_ubuntu-latest: - name: sdk-extension-aws-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-sdk-extension-aws-1 -- -ra - - py311-test-sdk-extension-aws-0_ubuntu-latest: - name: sdk-extension-aws-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-sdk-extension-aws-0 -- -ra - - py311-test-sdk-extension-aws-1_ubuntu-latest: - name: sdk-extension-aws-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-sdk-extension-aws-1 -- -ra - - py312-test-sdk-extension-aws-0_ubuntu-latest: - name: sdk-extension-aws-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-sdk-extension-aws-0 -- -ra - - py312-test-sdk-extension-aws-1_ubuntu-latest: - name: sdk-extension-aws-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-sdk-extension-aws-1 -- -ra - - py313-test-sdk-extension-aws-0_ubuntu-latest: - name: sdk-extension-aws-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-sdk-extension-aws-0 -- -ra - - py313-test-sdk-extension-aws-1_ubuntu-latest: - name: sdk-extension-aws-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-sdk-extension-aws-1 -- -ra - - py314-test-sdk-extension-aws-0_ubuntu-latest: - name: sdk-extension-aws-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-sdk-extension-aws-0 -- -ra - - py314-test-sdk-extension-aws-1_ubuntu-latest: - name: sdk-extension-aws-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-sdk-extension-aws-1 -- -ra - - pypy3-test-sdk-extension-aws-0_ubuntu-latest: - name: sdk-extension-aws-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-sdk-extension-aws-0 -- -ra - - pypy3-test-sdk-extension-aws-1_ubuntu-latest: - name: sdk-extension-aws-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-sdk-extension-aws-1 -- -ra - - py39-test-distro_ubuntu-latest: - name: distro 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-distro -- -ra - - py310-test-distro_ubuntu-latest: - name: distro 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-distro -- -ra - - py311-test-distro_ubuntu-latest: - name: distro 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-distro -- -ra - - py312-test-distro_ubuntu-latest: - name: distro 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-distro -- -ra - - py313-test-distro_ubuntu-latest: - name: distro 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-distro -- -ra - - py314-test-distro_ubuntu-latest: - name: distro 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-distro -- -ra - - pypy3-test-distro_ubuntu-latest: - name: distro pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-distro -- -ra - - py39-test-opentelemetry-instrumentation_ubuntu-latest: - name: opentelemetry-instrumentation 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-opentelemetry-instrumentation -- -ra - - py310-test-opentelemetry-instrumentation_ubuntu-latest: - name: opentelemetry-instrumentation 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-opentelemetry-instrumentation -- -ra - - py311-test-opentelemetry-instrumentation_ubuntu-latest: - name: opentelemetry-instrumentation 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-opentelemetry-instrumentation -- -ra - - py312-test-opentelemetry-instrumentation_ubuntu-latest: - name: opentelemetry-instrumentation 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-opentelemetry-instrumentation -- -ra - - py313-test-opentelemetry-instrumentation_ubuntu-latest: - name: opentelemetry-instrumentation 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-opentelemetry-instrumentation -- -ra - - py314-test-opentelemetry-instrumentation_ubuntu-latest: - name: opentelemetry-instrumentation 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-opentelemetry-instrumentation -- -ra - - pypy3-test-opentelemetry-instrumentation_ubuntu-latest: - name: opentelemetry-instrumentation pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-opentelemetry-instrumentation -- -ra - - py39-test-instrumentation-aiohttp-client_ubuntu-latest: - name: instrumentation-aiohttp-client 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aiohttp-client -- -ra - - py310-test-instrumentation-aiohttp-client_ubuntu-latest: - name: instrumentation-aiohttp-client 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aiohttp-client -- -ra - - py311-test-instrumentation-aiohttp-client_ubuntu-latest: - name: instrumentation-aiohttp-client 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aiohttp-client -- -ra - - py312-test-instrumentation-aiohttp-client_ubuntu-latest: - name: instrumentation-aiohttp-client 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aiohttp-client -- -ra - - py313-test-instrumentation-aiohttp-client_ubuntu-latest: - name: instrumentation-aiohttp-client 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aiohttp-client -- -ra - - py314-test-instrumentation-aiohttp-client_ubuntu-latest: - name: instrumentation-aiohttp-client 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aiohttp-client -- -ra - - pypy3-test-instrumentation-aiohttp-client_ubuntu-latest: - name: instrumentation-aiohttp-client pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aiohttp-client -- -ra - - py39-test-instrumentation-aiohttp-server_ubuntu-latest: - name: instrumentation-aiohttp-server 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aiohttp-server -- -ra - - py310-test-instrumentation-aiohttp-server_ubuntu-latest: - name: instrumentation-aiohttp-server 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aiohttp-server -- -ra - - py311-test-instrumentation-aiohttp-server_ubuntu-latest: - name: instrumentation-aiohttp-server 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aiohttp-server -- -ra - - py312-test-instrumentation-aiohttp-server_ubuntu-latest: - name: instrumentation-aiohttp-server 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aiohttp-server -- -ra - - py313-test-instrumentation-aiohttp-server_ubuntu-latest: - name: instrumentation-aiohttp-server 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aiohttp-server -- -ra - - py314-test-instrumentation-aiohttp-server_ubuntu-latest: - name: instrumentation-aiohttp-server 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aiohttp-server -- -ra - - pypy3-test-instrumentation-aiohttp-server_ubuntu-latest: - name: instrumentation-aiohttp-server pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aiohttp-server -- -ra - - py39-test-instrumentation-aiopg_ubuntu-latest: - name: instrumentation-aiopg 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aiopg -- -ra - - py310-test-instrumentation-aiopg_ubuntu-latest: - name: instrumentation-aiopg 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aiopg -- -ra - - py311-test-instrumentation-aiopg_ubuntu-latest: - name: instrumentation-aiopg 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aiopg -- -ra - - py312-test-instrumentation-aiopg_ubuntu-latest: - name: instrumentation-aiopg 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aiopg -- -ra - - py313-test-instrumentation-aiopg_ubuntu-latest: - name: instrumentation-aiopg 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aiopg -- -ra - - py314-test-instrumentation-aiopg_ubuntu-latest: - name: instrumentation-aiopg 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aiopg -- -ra - - py39-test-instrumentation-aws-lambda_ubuntu-latest: - name: instrumentation-aws-lambda 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aws-lambda -- -ra - - py310-test-instrumentation-aws-lambda_ubuntu-latest: - name: instrumentation-aws-lambda 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aws-lambda -- -ra - - py311-test-instrumentation-aws-lambda_ubuntu-latest: - name: instrumentation-aws-lambda 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aws-lambda -- -ra - - py312-test-instrumentation-aws-lambda_ubuntu-latest: - name: instrumentation-aws-lambda 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aws-lambda -- -ra - - py313-test-instrumentation-aws-lambda_ubuntu-latest: - name: instrumentation-aws-lambda 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aws-lambda -- -ra - - py314-test-instrumentation-aws-lambda_ubuntu-latest: - name: instrumentation-aws-lambda 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aws-lambda -- -ra - - pypy3-test-instrumentation-aws-lambda_ubuntu-latest: - name: instrumentation-aws-lambda pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aws-lambda -- -ra - - py39-test-instrumentation-botocore-0_ubuntu-latest: - name: instrumentation-botocore-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-botocore-0 -- -ra - - py39-test-instrumentation-botocore-1_ubuntu-latest: - name: instrumentation-botocore-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-botocore-1 -- -ra - - py310-test-instrumentation-botocore-0_ubuntu-latest: - name: instrumentation-botocore-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-botocore-0 -- -ra - - py310-test-instrumentation-botocore-1_ubuntu-latest: - name: instrumentation-botocore-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-botocore-1 -- -ra - - py311-test-instrumentation-botocore-0_ubuntu-latest: - name: instrumentation-botocore-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-botocore-0 -- -ra - - py311-test-instrumentation-botocore-1_ubuntu-latest: - name: instrumentation-botocore-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-botocore-1 -- -ra - - py312-test-instrumentation-botocore-0_ubuntu-latest: - name: instrumentation-botocore-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-botocore-0 -- -ra - - py312-test-instrumentation-botocore-1_ubuntu-latest: - name: instrumentation-botocore-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-botocore-1 -- -ra - - py313-test-instrumentation-botocore-0_ubuntu-latest: - name: instrumentation-botocore-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-botocore-0 -- -ra - - py313-test-instrumentation-botocore-1_ubuntu-latest: - name: instrumentation-botocore-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-botocore-1 -- -ra - - py314-test-instrumentation-botocore-0_ubuntu-latest: - name: instrumentation-botocore-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-botocore-0 -- -ra - - py314-test-instrumentation-botocore-1_ubuntu-latest: - name: instrumentation-botocore-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-botocore-1 -- -ra - - py39-test-instrumentation-boto3sqs_ubuntu-latest: - name: instrumentation-boto3sqs 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-boto3sqs -- -ra - - py310-test-instrumentation-boto3sqs_ubuntu-latest: - name: instrumentation-boto3sqs 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-boto3sqs -- -ra - - py311-test-instrumentation-boto3sqs_ubuntu-latest: - name: instrumentation-boto3sqs 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-boto3sqs -- -ra - - py312-test-instrumentation-boto3sqs_ubuntu-latest: - name: instrumentation-boto3sqs 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-boto3sqs -- -ra - - py313-test-instrumentation-boto3sqs_ubuntu-latest: - name: instrumentation-boto3sqs 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-boto3sqs -- -ra - - py314-test-instrumentation-boto3sqs_ubuntu-latest: - name: instrumentation-boto3sqs 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-boto3sqs -- -ra - - pypy3-test-instrumentation-boto3sqs_ubuntu-latest: - name: instrumentation-boto3sqs pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-boto3sqs -- -ra - - py39-test-instrumentation-django-0_ubuntu-latest: - name: instrumentation-django-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-django-0 -- -ra - - py39-test-instrumentation-django-1_ubuntu-latest: - name: instrumentation-django-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-django-1 -- -ra - - py39-test-instrumentation-django-2_ubuntu-latest: - name: instrumentation-django-2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-django-2 -- -ra - - py310-test-instrumentation-django-1_ubuntu-latest: - name: instrumentation-django-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-django-1 -- -ra - - py310-test-instrumentation-django-3_ubuntu-latest: - name: instrumentation-django-3 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-django-3 -- -ra - - py311-test-instrumentation-django-1_ubuntu-latest: - name: instrumentation-django-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-django-1 -- -ra - - py311-test-instrumentation-django-3_ubuntu-latest: - name: instrumentation-django-3 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-django-3 -- -ra - - py312-test-instrumentation-django-1_ubuntu-latest: - name: instrumentation-django-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-django-1 -- -ra - - py312-test-instrumentation-django-3_ubuntu-latest: - name: instrumentation-django-3 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-django-3 -- -ra - - py313-test-instrumentation-django-3_ubuntu-latest: - name: instrumentation-django-3 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-django-3 -- -ra - - py314-test-instrumentation-django-3_ubuntu-latest: - name: instrumentation-django-3 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-django-3 -- -ra - - pypy3-test-instrumentation-django-0_ubuntu-latest: - name: instrumentation-django-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-django-0 -- -ra - - pypy3-test-instrumentation-django-1_ubuntu-latest: - name: instrumentation-django-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-django-1 -- -ra - - py39-test-instrumentation-dbapi_ubuntu-latest: - name: instrumentation-dbapi 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-dbapi -- -ra - - py310-test-instrumentation-dbapi_ubuntu-latest: - name: instrumentation-dbapi 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-dbapi -- -ra - - py311-test-instrumentation-dbapi_ubuntu-latest: - name: instrumentation-dbapi 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-dbapi -- -ra - - py312-test-instrumentation-dbapi_ubuntu-latest: - name: instrumentation-dbapi 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-dbapi -- -ra - - py313-test-instrumentation-dbapi_ubuntu-latest: - name: instrumentation-dbapi 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-dbapi -- -ra - - py314-test-instrumentation-dbapi_ubuntu-latest: - name: instrumentation-dbapi 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-dbapi -- -ra - - pypy3-test-instrumentation-dbapi_ubuntu-latest: - name: instrumentation-dbapi pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-dbapi -- -ra - - py39-test-instrumentation-boto_ubuntu-latest: - name: instrumentation-boto 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-boto -- -ra - - py310-test-instrumentation-boto_ubuntu-latest: - name: instrumentation-boto 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-boto -- -ra - - py311-test-instrumentation-boto_ubuntu-latest: - name: instrumentation-boto 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-boto -- -ra - - py39-test-instrumentation-asyncclick_ubuntu-latest: - name: instrumentation-asyncclick 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-asyncclick -- -ra - - py310-test-instrumentation-asyncclick_ubuntu-latest: - name: instrumentation-asyncclick 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-asyncclick -- -ra - - py311-test-instrumentation-asyncclick_ubuntu-latest: - name: instrumentation-asyncclick 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-asyncclick -- -ra - - py312-test-instrumentation-asyncclick_ubuntu-latest: - name: instrumentation-asyncclick 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-asyncclick -- -ra - - py313-test-instrumentation-asyncclick_ubuntu-latest: - name: instrumentation-asyncclick 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-asyncclick -- -ra - - py314-test-instrumentation-asyncclick_ubuntu-latest: - name: instrumentation-asyncclick 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-asyncclick -- -ra - - pypy3-test-instrumentation-asyncclick_ubuntu-latest: - name: instrumentation-asyncclick pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-asyncclick -- -ra - - py39-test-instrumentation-click_ubuntu-latest: - name: instrumentation-click 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-click -- -ra - - py310-test-instrumentation-click_ubuntu-latest: - name: instrumentation-click 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-click -- -ra - - py311-test-instrumentation-click_ubuntu-latest: - name: instrumentation-click 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-click -- -ra - - py312-test-instrumentation-click_ubuntu-latest: - name: instrumentation-click 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-click -- -ra - - py313-test-instrumentation-click_ubuntu-latest: - name: instrumentation-click 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-click -- -ra - - py314-test-instrumentation-click_ubuntu-latest: - name: instrumentation-click 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-click -- -ra - - pypy3-test-instrumentation-click_ubuntu-latest: - name: instrumentation-click pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-click -- -ra - - py39-test-instrumentation-elasticsearch-0_ubuntu-latest: - name: instrumentation-elasticsearch-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-elasticsearch-0 -- -ra - - py39-test-instrumentation-elasticsearch-1_ubuntu-latest: - name: instrumentation-elasticsearch-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-elasticsearch-1 -- -ra - - py39-test-instrumentation-elasticsearch-2_ubuntu-latest: - name: instrumentation-elasticsearch-2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-elasticsearch-2 -- -ra - - py310-test-instrumentation-elasticsearch-0_ubuntu-latest: - name: instrumentation-elasticsearch-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-elasticsearch-0 -- -ra - - py310-test-instrumentation-elasticsearch-1_ubuntu-latest: - name: instrumentation-elasticsearch-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-elasticsearch-1 -- -ra - - py310-test-instrumentation-elasticsearch-2_ubuntu-latest: - name: instrumentation-elasticsearch-2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-elasticsearch-2 -- -ra - - py311-test-instrumentation-elasticsearch-0_ubuntu-latest: - name: instrumentation-elasticsearch-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-elasticsearch-0 -- -ra - - py311-test-instrumentation-elasticsearch-1_ubuntu-latest: - name: instrumentation-elasticsearch-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-elasticsearch-1 -- -ra - - py311-test-instrumentation-elasticsearch-2_ubuntu-latest: - name: instrumentation-elasticsearch-2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-elasticsearch-2 -- -ra - - py312-test-instrumentation-elasticsearch-0_ubuntu-latest: - name: instrumentation-elasticsearch-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-elasticsearch-0 -- -ra - - py312-test-instrumentation-elasticsearch-1_ubuntu-latest: - name: instrumentation-elasticsearch-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-elasticsearch-1 -- -ra - - py312-test-instrumentation-elasticsearch-2_ubuntu-latest: - name: instrumentation-elasticsearch-2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-elasticsearch-2 -- -ra - - py313-test-instrumentation-elasticsearch-0_ubuntu-latest: - name: instrumentation-elasticsearch-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-elasticsearch-0 -- -ra - - py313-test-instrumentation-elasticsearch-1_ubuntu-latest: - name: instrumentation-elasticsearch-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-elasticsearch-1 -- -ra - - py313-test-instrumentation-elasticsearch-2_ubuntu-latest: - name: instrumentation-elasticsearch-2 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-elasticsearch-2 -- -ra - - py314-test-instrumentation-elasticsearch-0_ubuntu-latest: - name: instrumentation-elasticsearch-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-elasticsearch-0 -- -ra - - py314-test-instrumentation-elasticsearch-1_ubuntu-latest: - name: instrumentation-elasticsearch-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-elasticsearch-1 -- -ra - - py314-test-instrumentation-elasticsearch-2_ubuntu-latest: - name: instrumentation-elasticsearch-2 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-elasticsearch-2 -- -ra - - pypy3-test-instrumentation-elasticsearch-0_ubuntu-latest: - name: instrumentation-elasticsearch-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-elasticsearch-0 -- -ra - - pypy3-test-instrumentation-elasticsearch-1_ubuntu-latest: - name: instrumentation-elasticsearch-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-elasticsearch-1 -- -ra - - pypy3-test-instrumentation-elasticsearch-2_ubuntu-latest: - name: instrumentation-elasticsearch-2 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-elasticsearch-2 -- -ra - - py39-test-instrumentation-falcon-0_ubuntu-latest: - name: instrumentation-falcon-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-falcon-0 -- -ra - - py39-test-instrumentation-falcon-1_ubuntu-latest: - name: instrumentation-falcon-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-falcon-1 -- -ra - - py39-test-instrumentation-falcon-2_ubuntu-latest: - name: instrumentation-falcon-2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-falcon-2 -- -ra - - py39-test-instrumentation-falcon-3_ubuntu-latest: - name: instrumentation-falcon-3 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-falcon-3 -- -ra - - py310-test-instrumentation-falcon-1_ubuntu-latest: - name: instrumentation-falcon-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-falcon-1 -- -ra - - py310-test-instrumentation-falcon-2_ubuntu-latest: - name: instrumentation-falcon-2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-falcon-2 -- -ra - - py310-test-instrumentation-falcon-3_ubuntu-latest: - name: instrumentation-falcon-3 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-falcon-3 -- -ra - - py310-test-instrumentation-falcon-4_ubuntu-latest: - name: instrumentation-falcon-4 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-falcon-4 -- -ra - - py311-test-instrumentation-falcon-1_ubuntu-latest: - name: instrumentation-falcon-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-falcon-1 -- -ra - - py311-test-instrumentation-falcon-2_ubuntu-latest: - name: instrumentation-falcon-2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-falcon-2 -- -ra - - py311-test-instrumentation-falcon-3_ubuntu-latest: - name: instrumentation-falcon-3 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-falcon-3 -- -ra - - py311-test-instrumentation-falcon-4_ubuntu-latest: - name: instrumentation-falcon-4 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-falcon-4 -- -ra - - py312-test-instrumentation-falcon-1_ubuntu-latest: - name: instrumentation-falcon-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-falcon-1 -- -ra - - py312-test-instrumentation-falcon-2_ubuntu-latest: - name: instrumentation-falcon-2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-falcon-2 -- -ra - - py312-test-instrumentation-falcon-3_ubuntu-latest: - name: instrumentation-falcon-3 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-falcon-3 -- -ra - - py312-test-instrumentation-falcon-4_ubuntu-latest: - name: instrumentation-falcon-4 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-falcon-4 -- -ra - - py313-test-instrumentation-falcon-4_ubuntu-latest: - name: instrumentation-falcon-4 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-falcon-4 -- -ra - - py314-test-instrumentation-falcon-4_ubuntu-latest: - name: instrumentation-falcon-4 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-falcon-4 -- -ra - - pypy3-test-instrumentation-falcon-0_ubuntu-latest: - name: instrumentation-falcon-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-falcon-0 -- -ra - - pypy3-test-instrumentation-falcon-1_ubuntu-latest: - name: instrumentation-falcon-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-falcon-1 -- -ra - - pypy3-test-instrumentation-falcon-2_ubuntu-latest: - name: instrumentation-falcon-2 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-falcon-2 -- -ra - - pypy3-test-instrumentation-falcon-3_ubuntu-latest: - name: instrumentation-falcon-3 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-falcon-3 -- -ra - - pypy3-test-instrumentation-falcon-4_ubuntu-latest: - name: instrumentation-falcon-4 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-falcon-4 -- -ra - - py39-test-instrumentation-fastapi_ubuntu-latest: - name: instrumentation-fastapi 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-fastapi -- -ra - - py310-test-instrumentation-fastapi_ubuntu-latest: - name: instrumentation-fastapi 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-fastapi -- -ra - - py311-test-instrumentation-fastapi_ubuntu-latest: - name: instrumentation-fastapi 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-fastapi -- -ra - - py312-test-instrumentation-fastapi_ubuntu-latest: - name: instrumentation-fastapi 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-fastapi -- -ra - - py313-test-instrumentation-fastapi_ubuntu-latest: - name: instrumentation-fastapi 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-fastapi -- -ra - - py314-test-instrumentation-fastapi_ubuntu-latest: - name: instrumentation-fastapi 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-fastapi -- -ra diff --git a/.github/workflows/test_1.yml b/.github/workflows/test_1.yml deleted file mode 100644 index 3da7168646..0000000000 --- a/.github/workflows/test_1.yml +++ /dev/null @@ -1,4783 +0,0 @@ -# Do not edit this file. -# This file is generated automatically by executing tox -e generate-workflows - -name: Test 1 - -on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -env: - # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' - # For PRs you can change the inner fallback ('main') - # For pushes you change the outer fallback ('main') - # The logic below is used during releases and depends on having an equivalent branch name in the core repo. - CORE_REPO_SHA: ${{ github.event_name == 'pull_request' && ( - contains(github.event.pull_request.labels.*.name, 'prepare-release') && github.event.pull_request.head.ref || - contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref || - 'main' - ) || 'main' }} - CONTRIB_REPO_SHA: main - PIP_EXISTS_ACTION: w - -jobs: - - pypy3-test-instrumentation-fastapi_ubuntu-latest: - name: instrumentation-fastapi pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-fastapi -- -ra - - py39-test-instrumentation-flask-0_ubuntu-latest: - name: instrumentation-flask-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-flask-0 -- -ra - - py39-test-instrumentation-flask-1_ubuntu-latest: - name: instrumentation-flask-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-flask-1 -- -ra - - py39-test-instrumentation-flask-2_ubuntu-latest: - name: instrumentation-flask-2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-flask-2 -- -ra - - py39-test-instrumentation-flask-3_ubuntu-latest: - name: instrumentation-flask-3 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-flask-3 -- -ra - - py310-test-instrumentation-flask-0_ubuntu-latest: - name: instrumentation-flask-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-flask-0 -- -ra - - py310-test-instrumentation-flask-1_ubuntu-latest: - name: instrumentation-flask-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-flask-1 -- -ra - - py310-test-instrumentation-flask-2_ubuntu-latest: - name: instrumentation-flask-2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-flask-2 -- -ra - - py310-test-instrumentation-flask-3_ubuntu-latest: - name: instrumentation-flask-3 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-flask-3 -- -ra - - py311-test-instrumentation-flask-0_ubuntu-latest: - name: instrumentation-flask-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-flask-0 -- -ra - - py311-test-instrumentation-flask-1_ubuntu-latest: - name: instrumentation-flask-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-flask-1 -- -ra - - py311-test-instrumentation-flask-2_ubuntu-latest: - name: instrumentation-flask-2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-flask-2 -- -ra - - py311-test-instrumentation-flask-3_ubuntu-latest: - name: instrumentation-flask-3 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-flask-3 -- -ra - - py312-test-instrumentation-flask-0_ubuntu-latest: - name: instrumentation-flask-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-flask-0 -- -ra - - py312-test-instrumentation-flask-1_ubuntu-latest: - name: instrumentation-flask-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-flask-1 -- -ra - - py312-test-instrumentation-flask-2_ubuntu-latest: - name: instrumentation-flask-2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-flask-2 -- -ra - - py312-test-instrumentation-flask-3_ubuntu-latest: - name: instrumentation-flask-3 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-flask-3 -- -ra - - py313-test-instrumentation-flask-0_ubuntu-latest: - name: instrumentation-flask-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-flask-0 -- -ra - - py313-test-instrumentation-flask-1_ubuntu-latest: - name: instrumentation-flask-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-flask-1 -- -ra - - py313-test-instrumentation-flask-2_ubuntu-latest: - name: instrumentation-flask-2 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-flask-2 -- -ra - - py313-test-instrumentation-flask-3_ubuntu-latest: - name: instrumentation-flask-3 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-flask-3 -- -ra - - py314-test-instrumentation-flask-0_ubuntu-latest: - name: instrumentation-flask-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-flask-0 -- -ra - - py314-test-instrumentation-flask-1_ubuntu-latest: - name: instrumentation-flask-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-flask-1 -- -ra - - py314-test-instrumentation-flask-2_ubuntu-latest: - name: instrumentation-flask-2 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-flask-2 -- -ra - - py314-test-instrumentation-flask-3_ubuntu-latest: - name: instrumentation-flask-3 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-flask-3 -- -ra - - pypy3-test-instrumentation-flask-0_ubuntu-latest: - name: instrumentation-flask-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-flask-0 -- -ra - - pypy3-test-instrumentation-flask-1_ubuntu-latest: - name: instrumentation-flask-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-flask-1 -- -ra - - py39-test-instrumentation-urllib_ubuntu-latest: - name: instrumentation-urllib 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-urllib -- -ra - - py310-test-instrumentation-urllib_ubuntu-latest: - name: instrumentation-urllib 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-urllib -- -ra - - py311-test-instrumentation-urllib_ubuntu-latest: - name: instrumentation-urllib 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-urllib -- -ra - - py312-test-instrumentation-urllib_ubuntu-latest: - name: instrumentation-urllib 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-urllib -- -ra - - py313-test-instrumentation-urllib_ubuntu-latest: - name: instrumentation-urllib 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-urllib -- -ra - - py314-test-instrumentation-urllib_ubuntu-latest: - name: instrumentation-urllib 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-urllib -- -ra - - pypy3-test-instrumentation-urllib_ubuntu-latest: - name: instrumentation-urllib pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-urllib -- -ra - - py39-test-instrumentation-urllib3-0_ubuntu-latest: - name: instrumentation-urllib3-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-urllib3-0 -- -ra - - py39-test-instrumentation-urllib3-1_ubuntu-latest: - name: instrumentation-urllib3-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-urllib3-1 -- -ra - - py310-test-instrumentation-urllib3-0_ubuntu-latest: - name: instrumentation-urllib3-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-urllib3-0 -- -ra - - py310-test-instrumentation-urllib3-1_ubuntu-latest: - name: instrumentation-urllib3-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-urllib3-1 -- -ra - - py311-test-instrumentation-urllib3-0_ubuntu-latest: - name: instrumentation-urllib3-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-urllib3-0 -- -ra - - py311-test-instrumentation-urllib3-1_ubuntu-latest: - name: instrumentation-urllib3-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-urllib3-1 -- -ra - - py312-test-instrumentation-urllib3-0_ubuntu-latest: - name: instrumentation-urllib3-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-urllib3-0 -- -ra - - py312-test-instrumentation-urllib3-1_ubuntu-latest: - name: instrumentation-urllib3-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-urllib3-1 -- -ra - - py313-test-instrumentation-urllib3-0_ubuntu-latest: - name: instrumentation-urllib3-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-urllib3-0 -- -ra - - py313-test-instrumentation-urllib3-1_ubuntu-latest: - name: instrumentation-urllib3-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-urllib3-1 -- -ra - - pypy3-test-instrumentation-urllib3-0_ubuntu-latest: - name: instrumentation-urllib3-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-urllib3-0 -- -ra - - pypy3-test-instrumentation-urllib3-1_ubuntu-latest: - name: instrumentation-urllib3-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-urllib3-1 -- -ra - - py39-test-instrumentation-requests_ubuntu-latest: - name: instrumentation-requests 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-requests -- -ra - - py310-test-instrumentation-requests_ubuntu-latest: - name: instrumentation-requests 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-requests -- -ra - - py311-test-instrumentation-requests_ubuntu-latest: - name: instrumentation-requests 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-requests -- -ra - - py312-test-instrumentation-requests_ubuntu-latest: - name: instrumentation-requests 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-requests -- -ra - - py313-test-instrumentation-requests_ubuntu-latest: - name: instrumentation-requests 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-requests -- -ra - - py314-test-instrumentation-requests_ubuntu-latest: - name: instrumentation-requests 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-requests -- -ra - - py39-test-instrumentation-starlette-oldest_ubuntu-latest: - name: instrumentation-starlette-oldest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-starlette-oldest -- -ra - - py39-test-instrumentation-starlette-latest_ubuntu-latest: - name: instrumentation-starlette-latest 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-starlette-latest -- -ra - - py310-test-instrumentation-starlette-oldest_ubuntu-latest: - name: instrumentation-starlette-oldest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-starlette-oldest -- -ra - - py310-test-instrumentation-starlette-latest_ubuntu-latest: - name: instrumentation-starlette-latest 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-starlette-latest -- -ra - - py311-test-instrumentation-starlette-oldest_ubuntu-latest: - name: instrumentation-starlette-oldest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-starlette-oldest -- -ra - - py311-test-instrumentation-starlette-latest_ubuntu-latest: - name: instrumentation-starlette-latest 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-starlette-latest -- -ra - - py312-test-instrumentation-starlette-oldest_ubuntu-latest: - name: instrumentation-starlette-oldest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-starlette-oldest -- -ra - - py312-test-instrumentation-starlette-latest_ubuntu-latest: - name: instrumentation-starlette-latest 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-starlette-latest -- -ra - - py313-test-instrumentation-starlette-oldest_ubuntu-latest: - name: instrumentation-starlette-oldest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-starlette-oldest -- -ra - - py313-test-instrumentation-starlette-latest_ubuntu-latest: - name: instrumentation-starlette-latest 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-starlette-latest -- -ra - - py314-test-instrumentation-starlette-oldest_ubuntu-latest: - name: instrumentation-starlette-oldest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-starlette-oldest -- -ra - - py314-test-instrumentation-starlette-latest_ubuntu-latest: - name: instrumentation-starlette-latest 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-starlette-latest -- -ra - - pypy3-test-instrumentation-starlette-oldest_ubuntu-latest: - name: instrumentation-starlette-oldest pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-starlette-oldest -- -ra - - pypy3-test-instrumentation-starlette-latest_ubuntu-latest: - name: instrumentation-starlette-latest pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-starlette-latest -- -ra - - py39-test-instrumentation-jinja2_ubuntu-latest: - name: instrumentation-jinja2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-jinja2 -- -ra - - py310-test-instrumentation-jinja2_ubuntu-latest: - name: instrumentation-jinja2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-jinja2 -- -ra - - py311-test-instrumentation-jinja2_ubuntu-latest: - name: instrumentation-jinja2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-jinja2 -- -ra - - py312-test-instrumentation-jinja2_ubuntu-latest: - name: instrumentation-jinja2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-jinja2 -- -ra - - py313-test-instrumentation-jinja2_ubuntu-latest: - name: instrumentation-jinja2 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-jinja2 -- -ra - - py314-test-instrumentation-jinja2_ubuntu-latest: - name: instrumentation-jinja2 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-jinja2 -- -ra - - pypy3-test-instrumentation-jinja2_ubuntu-latest: - name: instrumentation-jinja2 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-jinja2 -- -ra - - py39-test-instrumentation-logging_ubuntu-latest: - name: instrumentation-logging 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-logging -- -ra - - py310-test-instrumentation-logging_ubuntu-latest: - name: instrumentation-logging 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-logging -- -ra - - py311-test-instrumentation-logging_ubuntu-latest: - name: instrumentation-logging 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-logging -- -ra - - py312-test-instrumentation-logging_ubuntu-latest: - name: instrumentation-logging 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-logging -- -ra - - py313-test-instrumentation-logging_ubuntu-latest: - name: instrumentation-logging 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-logging -- -ra - - py314-test-instrumentation-logging_ubuntu-latest: - name: instrumentation-logging 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-logging -- -ra - - pypy3-test-instrumentation-logging_ubuntu-latest: - name: instrumentation-logging pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-logging -- -ra - - py39-test-exporter-richconsole_ubuntu-latest: - name: exporter-richconsole 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-exporter-richconsole -- -ra - - py310-test-exporter-richconsole_ubuntu-latest: - name: exporter-richconsole 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-exporter-richconsole -- -ra - - py311-test-exporter-richconsole_ubuntu-latest: - name: exporter-richconsole 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-exporter-richconsole -- -ra - - py312-test-exporter-richconsole_ubuntu-latest: - name: exporter-richconsole 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-exporter-richconsole -- -ra - - py313-test-exporter-richconsole_ubuntu-latest: - name: exporter-richconsole 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-exporter-richconsole -- -ra - - py314-test-exporter-richconsole_ubuntu-latest: - name: exporter-richconsole 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-exporter-richconsole -- -ra - - pypy3-test-exporter-richconsole_ubuntu-latest: - name: exporter-richconsole pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-exporter-richconsole -- -ra - - py39-test-exporter-prometheus-remote-write_ubuntu-latest: - name: exporter-prometheus-remote-write 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-exporter-prometheus-remote-write -- -ra - - py310-test-exporter-prometheus-remote-write_ubuntu-latest: - name: exporter-prometheus-remote-write 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-exporter-prometheus-remote-write -- -ra - - py311-test-exporter-prometheus-remote-write_ubuntu-latest: - name: exporter-prometheus-remote-write 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-exporter-prometheus-remote-write -- -ra - - py312-test-exporter-prometheus-remote-write_ubuntu-latest: - name: exporter-prometheus-remote-write 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-exporter-prometheus-remote-write -- -ra - - py313-test-exporter-prometheus-remote-write_ubuntu-latest: - name: exporter-prometheus-remote-write 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-exporter-prometheus-remote-write -- -ra - - py314-test-exporter-prometheus-remote-write_ubuntu-latest: - name: exporter-prometheus-remote-write 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-exporter-prometheus-remote-write -- -ra - - pypy310-test-exporter-prometheus-remote-write_ubuntu-latest: - name: exporter-prometheus-remote-write pypy-3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.10 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy310-test-exporter-prometheus-remote-write -- -ra - - py39-test-instrumentation-mysql-0_ubuntu-latest: - name: instrumentation-mysql-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-mysql-0 -- -ra - - py39-test-instrumentation-mysql-1_ubuntu-latest: - name: instrumentation-mysql-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-mysql-1 -- -ra - - py310-test-instrumentation-mysql-0_ubuntu-latest: - name: instrumentation-mysql-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-mysql-0 -- -ra - - py310-test-instrumentation-mysql-1_ubuntu-latest: - name: instrumentation-mysql-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-mysql-1 -- -ra - - py311-test-instrumentation-mysql-0_ubuntu-latest: - name: instrumentation-mysql-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-mysql-0 -- -ra - - py311-test-instrumentation-mysql-1_ubuntu-latest: - name: instrumentation-mysql-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-mysql-1 -- -ra - - py312-test-instrumentation-mysql-0_ubuntu-latest: - name: instrumentation-mysql-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-mysql-0 -- -ra - - py312-test-instrumentation-mysql-1_ubuntu-latest: - name: instrumentation-mysql-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-mysql-1 -- -ra - - py313-test-instrumentation-mysql-0_ubuntu-latest: - name: instrumentation-mysql-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-mysql-0 -- -ra - - py313-test-instrumentation-mysql-1_ubuntu-latest: - name: instrumentation-mysql-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-mysql-1 -- -ra - - py314-test-instrumentation-mysql-0_ubuntu-latest: - name: instrumentation-mysql-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-mysql-0 -- -ra - - py314-test-instrumentation-mysql-1_ubuntu-latest: - name: instrumentation-mysql-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-mysql-1 -- -ra - - pypy3-test-instrumentation-mysql-0_ubuntu-latest: - name: instrumentation-mysql-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-mysql-0 -- -ra - - pypy3-test-instrumentation-mysql-1_ubuntu-latest: - name: instrumentation-mysql-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-mysql-1 -- -ra - - py39-test-instrumentation-mysqlclient_ubuntu-latest: - name: instrumentation-mysqlclient 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-mysqlclient -- -ra - - py310-test-instrumentation-mysqlclient_ubuntu-latest: - name: instrumentation-mysqlclient 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-mysqlclient -- -ra - - py311-test-instrumentation-mysqlclient_ubuntu-latest: - name: instrumentation-mysqlclient 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-mysqlclient -- -ra - - py312-test-instrumentation-mysqlclient_ubuntu-latest: - name: instrumentation-mysqlclient 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-mysqlclient -- -ra - - py313-test-instrumentation-mysqlclient_ubuntu-latest: - name: instrumentation-mysqlclient 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-mysqlclient -- -ra - - py314-test-instrumentation-mysqlclient_ubuntu-latest: - name: instrumentation-mysqlclient 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-mysqlclient -- -ra - - pypy3-test-instrumentation-mysqlclient_ubuntu-latest: - name: instrumentation-mysqlclient pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-mysqlclient -- -ra - - py39-test-instrumentation-psycopg2_ubuntu-latest: - name: instrumentation-psycopg2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-psycopg2 -- -ra - - py310-test-instrumentation-psycopg2_ubuntu-latest: - name: instrumentation-psycopg2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-psycopg2 -- -ra - - py311-test-instrumentation-psycopg2_ubuntu-latest: - name: instrumentation-psycopg2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-psycopg2 -- -ra - - py312-test-instrumentation-psycopg2_ubuntu-latest: - name: instrumentation-psycopg2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-psycopg2 -- -ra - - py313-test-instrumentation-psycopg2_ubuntu-latest: - name: instrumentation-psycopg2 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-psycopg2 -- -ra - - py314-test-instrumentation-psycopg2_ubuntu-latest: - name: instrumentation-psycopg2 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-psycopg2 -- -ra - - py39-test-instrumentation-psycopg2-binary_ubuntu-latest: - name: instrumentation-psycopg2-binary 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-psycopg2-binary -- -ra - - py310-test-instrumentation-psycopg2-binary_ubuntu-latest: - name: instrumentation-psycopg2-binary 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-psycopg2-binary -- -ra - - py311-test-instrumentation-psycopg2-binary_ubuntu-latest: - name: instrumentation-psycopg2-binary 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-psycopg2-binary -- -ra - - py312-test-instrumentation-psycopg2-binary_ubuntu-latest: - name: instrumentation-psycopg2-binary 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-psycopg2-binary -- -ra - - py313-test-instrumentation-psycopg2-binary_ubuntu-latest: - name: instrumentation-psycopg2-binary 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-psycopg2-binary -- -ra - - py314-test-instrumentation-psycopg2-binary_ubuntu-latest: - name: instrumentation-psycopg2-binary 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-psycopg2-binary -- -ra - - py39-test-instrumentation-psycopg_ubuntu-latest: - name: instrumentation-psycopg 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-psycopg -- -ra - - py310-test-instrumentation-psycopg_ubuntu-latest: - name: instrumentation-psycopg 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-psycopg -- -ra - - py311-test-instrumentation-psycopg_ubuntu-latest: - name: instrumentation-psycopg 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-psycopg -- -ra - - py312-test-instrumentation-psycopg_ubuntu-latest: - name: instrumentation-psycopg 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-psycopg -- -ra - - py313-test-instrumentation-psycopg_ubuntu-latest: - name: instrumentation-psycopg 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-psycopg -- -ra - - py314-test-instrumentation-psycopg_ubuntu-latest: - name: instrumentation-psycopg 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-psycopg -- -ra - - pypy3-test-instrumentation-psycopg_ubuntu-latest: - name: instrumentation-psycopg pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-psycopg -- -ra - - py39-test-instrumentation-pymemcache-0_ubuntu-latest: - name: instrumentation-pymemcache-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-0 -- -ra - - py39-test-instrumentation-pymemcache-1_ubuntu-latest: - name: instrumentation-pymemcache-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-1 -- -ra - - py39-test-instrumentation-pymemcache-2_ubuntu-latest: - name: instrumentation-pymemcache-2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-2 -- -ra - - py39-test-instrumentation-pymemcache-3_ubuntu-latest: - name: instrumentation-pymemcache-3 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-3 -- -ra - - py39-test-instrumentation-pymemcache-4_ubuntu-latest: - name: instrumentation-pymemcache-4 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymemcache-4 -- -ra - - py310-test-instrumentation-pymemcache-0_ubuntu-latest: - name: instrumentation-pymemcache-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymemcache-0 -- -ra - - py310-test-instrumentation-pymemcache-1_ubuntu-latest: - name: instrumentation-pymemcache-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymemcache-1 -- -ra - - py310-test-instrumentation-pymemcache-2_ubuntu-latest: - name: instrumentation-pymemcache-2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymemcache-2 -- -ra - - py310-test-instrumentation-pymemcache-3_ubuntu-latest: - name: instrumentation-pymemcache-3 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymemcache-3 -- -ra - - py310-test-instrumentation-pymemcache-4_ubuntu-latest: - name: instrumentation-pymemcache-4 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymemcache-4 -- -ra - - py311-test-instrumentation-pymemcache-0_ubuntu-latest: - name: instrumentation-pymemcache-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymemcache-0 -- -ra - - py311-test-instrumentation-pymemcache-1_ubuntu-latest: - name: instrumentation-pymemcache-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymemcache-1 -- -ra - - py311-test-instrumentation-pymemcache-2_ubuntu-latest: - name: instrumentation-pymemcache-2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymemcache-2 -- -ra - - py311-test-instrumentation-pymemcache-3_ubuntu-latest: - name: instrumentation-pymemcache-3 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymemcache-3 -- -ra - - py311-test-instrumentation-pymemcache-4_ubuntu-latest: - name: instrumentation-pymemcache-4 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymemcache-4 -- -ra - - py312-test-instrumentation-pymemcache-0_ubuntu-latest: - name: instrumentation-pymemcache-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymemcache-0 -- -ra - - py312-test-instrumentation-pymemcache-1_ubuntu-latest: - name: instrumentation-pymemcache-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymemcache-1 -- -ra - - py312-test-instrumentation-pymemcache-2_ubuntu-latest: - name: instrumentation-pymemcache-2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymemcache-2 -- -ra - - py312-test-instrumentation-pymemcache-3_ubuntu-latest: - name: instrumentation-pymemcache-3 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymemcache-3 -- -ra - - py312-test-instrumentation-pymemcache-4_ubuntu-latest: - name: instrumentation-pymemcache-4 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymemcache-4 -- -ra - - py313-test-instrumentation-pymemcache-0_ubuntu-latest: - name: instrumentation-pymemcache-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymemcache-0 -- -ra - - py313-test-instrumentation-pymemcache-1_ubuntu-latest: - name: instrumentation-pymemcache-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymemcache-1 -- -ra - - py313-test-instrumentation-pymemcache-2_ubuntu-latest: - name: instrumentation-pymemcache-2 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymemcache-2 -- -ra - - py313-test-instrumentation-pymemcache-3_ubuntu-latest: - name: instrumentation-pymemcache-3 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymemcache-3 -- -ra - - py313-test-instrumentation-pymemcache-4_ubuntu-latest: - name: instrumentation-pymemcache-4 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymemcache-4 -- -ra - - py314-test-instrumentation-pymemcache-0_ubuntu-latest: - name: instrumentation-pymemcache-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymemcache-0 -- -ra - - py314-test-instrumentation-pymemcache-1_ubuntu-latest: - name: instrumentation-pymemcache-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymemcache-1 -- -ra - - py314-test-instrumentation-pymemcache-2_ubuntu-latest: - name: instrumentation-pymemcache-2 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymemcache-2 -- -ra - - py314-test-instrumentation-pymemcache-3_ubuntu-latest: - name: instrumentation-pymemcache-3 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymemcache-3 -- -ra - - py314-test-instrumentation-pymemcache-4_ubuntu-latest: - name: instrumentation-pymemcache-4 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymemcache-4 -- -ra - - pypy3-test-instrumentation-pymemcache-0_ubuntu-latest: - name: instrumentation-pymemcache-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pymemcache-0 -- -ra - - pypy3-test-instrumentation-pymemcache-1_ubuntu-latest: - name: instrumentation-pymemcache-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pymemcache-1 -- -ra - - pypy3-test-instrumentation-pymemcache-2_ubuntu-latest: - name: instrumentation-pymemcache-2 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pymemcache-2 -- -ra - - pypy3-test-instrumentation-pymemcache-3_ubuntu-latest: - name: instrumentation-pymemcache-3 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pymemcache-3 -- -ra - - pypy3-test-instrumentation-pymemcache-4_ubuntu-latest: - name: instrumentation-pymemcache-4 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pymemcache-4 -- -ra - - py39-test-instrumentation-pymongo_ubuntu-latest: - name: instrumentation-pymongo 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymongo -- -ra - - py310-test-instrumentation-pymongo_ubuntu-latest: - name: instrumentation-pymongo 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymongo -- -ra - - py311-test-instrumentation-pymongo_ubuntu-latest: - name: instrumentation-pymongo 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymongo -- -ra - - py312-test-instrumentation-pymongo_ubuntu-latest: - name: instrumentation-pymongo 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymongo -- -ra - - py313-test-instrumentation-pymongo_ubuntu-latest: - name: instrumentation-pymongo 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymongo -- -ra - - py314-test-instrumentation-pymongo_ubuntu-latest: - name: instrumentation-pymongo 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymongo -- -ra - - pypy3-test-instrumentation-pymongo_ubuntu-latest: - name: instrumentation-pymongo pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pymongo -- -ra - - py39-test-instrumentation-pymysql_ubuntu-latest: - name: instrumentation-pymysql 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymysql -- -ra - - py310-test-instrumentation-pymysql_ubuntu-latest: - name: instrumentation-pymysql 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymysql -- -ra - - py311-test-instrumentation-pymysql_ubuntu-latest: - name: instrumentation-pymysql 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymysql -- -ra - - py312-test-instrumentation-pymysql_ubuntu-latest: - name: instrumentation-pymysql 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymysql -- -ra - - py313-test-instrumentation-pymysql_ubuntu-latest: - name: instrumentation-pymysql 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymysql -- -ra - - py314-test-instrumentation-pymysql_ubuntu-latest: - name: instrumentation-pymysql 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymysql -- -ra - - pypy3-test-instrumentation-pymysql_ubuntu-latest: - name: instrumentation-pymysql pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pymysql -- -ra - - py39-test-instrumentation-pymssql_ubuntu-latest: - name: instrumentation-pymssql 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pymssql -- -ra - - py310-test-instrumentation-pymssql_ubuntu-latest: - name: instrumentation-pymssql 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pymssql -- -ra - - py311-test-instrumentation-pymssql_ubuntu-latest: - name: instrumentation-pymssql 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pymssql -- -ra - - py312-test-instrumentation-pymssql_ubuntu-latest: - name: instrumentation-pymssql 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pymssql -- -ra - - py313-test-instrumentation-pymssql_ubuntu-latest: - name: instrumentation-pymssql 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pymssql -- -ra - - py314-test-instrumentation-pymssql_ubuntu-latest: - name: instrumentation-pymssql 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pymssql -- -ra - - py39-test-instrumentation-pyramid_ubuntu-latest: - name: instrumentation-pyramid 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-pyramid -- -ra - - py310-test-instrumentation-pyramid_ubuntu-latest: - name: instrumentation-pyramid 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-pyramid -- -ra - - py311-test-instrumentation-pyramid_ubuntu-latest: - name: instrumentation-pyramid 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-pyramid -- -ra - - py312-test-instrumentation-pyramid_ubuntu-latest: - name: instrumentation-pyramid 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-pyramid -- -ra - - py313-test-instrumentation-pyramid_ubuntu-latest: - name: instrumentation-pyramid 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-pyramid -- -ra - - py314-test-instrumentation-pyramid_ubuntu-latest: - name: instrumentation-pyramid 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-pyramid -- -ra - - pypy3-test-instrumentation-pyramid_ubuntu-latest: - name: instrumentation-pyramid pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-pyramid -- -ra - - py39-test-instrumentation-asgi_ubuntu-latest: - name: instrumentation-asgi 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-asgi -- -ra - - py310-test-instrumentation-asgi_ubuntu-latest: - name: instrumentation-asgi 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-asgi -- -ra - - py311-test-instrumentation-asgi_ubuntu-latest: - name: instrumentation-asgi 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-asgi -- -ra - - py312-test-instrumentation-asgi_ubuntu-latest: - name: instrumentation-asgi 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-asgi -- -ra - - py313-test-instrumentation-asgi_ubuntu-latest: - name: instrumentation-asgi 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-asgi -- -ra - - py314-test-instrumentation-asgi_ubuntu-latest: - name: instrumentation-asgi 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-asgi -- -ra - - pypy3-test-instrumentation-asgi_ubuntu-latest: - name: instrumentation-asgi pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-asgi -- -ra - - py39-test-instrumentation-asyncpg_ubuntu-latest: - name: instrumentation-asyncpg 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-asyncpg -- -ra - - py310-test-instrumentation-asyncpg_ubuntu-latest: - name: instrumentation-asyncpg 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-asyncpg -- -ra - - py311-test-instrumentation-asyncpg_ubuntu-latest: - name: instrumentation-asyncpg 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-asyncpg -- -ra - - py312-test-instrumentation-asyncpg_ubuntu-latest: - name: instrumentation-asyncpg 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-asyncpg -- -ra - - py313-test-instrumentation-asyncpg_ubuntu-latest: - name: instrumentation-asyncpg 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-asyncpg -- -ra - - py314-test-instrumentation-asyncpg_ubuntu-latest: - name: instrumentation-asyncpg 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-asyncpg -- -ra - - py39-test-instrumentation-sqlite3_ubuntu-latest: - name: instrumentation-sqlite3 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-sqlite3 -- -ra - - py310-test-instrumentation-sqlite3_ubuntu-latest: - name: instrumentation-sqlite3 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-sqlite3 -- -ra - - py311-test-instrumentation-sqlite3_ubuntu-latest: - name: instrumentation-sqlite3 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-sqlite3 -- -ra - - py312-test-instrumentation-sqlite3_ubuntu-latest: - name: instrumentation-sqlite3 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-sqlite3 -- -ra - - py313-test-instrumentation-sqlite3_ubuntu-latest: - name: instrumentation-sqlite3 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-sqlite3 -- -ra - - py314-test-instrumentation-sqlite3_ubuntu-latest: - name: instrumentation-sqlite3 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-sqlite3 -- -ra - - pypy3-test-instrumentation-sqlite3_ubuntu-latest: - name: instrumentation-sqlite3 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-sqlite3 -- -ra - - py39-test-instrumentation-wsgi_ubuntu-latest: - name: instrumentation-wsgi 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-wsgi -- -ra - - py310-test-instrumentation-wsgi_ubuntu-latest: - name: instrumentation-wsgi 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-wsgi -- -ra - - py311-test-instrumentation-wsgi_ubuntu-latest: - name: instrumentation-wsgi 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-wsgi -- -ra - - py312-test-instrumentation-wsgi_ubuntu-latest: - name: instrumentation-wsgi 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-wsgi -- -ra - - py313-test-instrumentation-wsgi_ubuntu-latest: - name: instrumentation-wsgi 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-wsgi -- -ra - - py314-test-instrumentation-wsgi_ubuntu-latest: - name: instrumentation-wsgi 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-wsgi -- -ra - - pypy3-test-instrumentation-wsgi_ubuntu-latest: - name: instrumentation-wsgi pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-wsgi -- -ra - - py39-test-instrumentation-grpc-0_ubuntu-latest: - name: instrumentation-grpc-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-grpc-0 -- -ra - - py39-test-instrumentation-grpc-1_ubuntu-latest: - name: instrumentation-grpc-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-grpc-1 -- -ra - - py310-test-instrumentation-grpc-0_ubuntu-latest: - name: instrumentation-grpc-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-grpc-0 -- -ra - - py310-test-instrumentation-grpc-1_ubuntu-latest: - name: instrumentation-grpc-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-grpc-1 -- -ra - - py311-test-instrumentation-grpc-0_ubuntu-latest: - name: instrumentation-grpc-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-grpc-0 -- -ra - - py311-test-instrumentation-grpc-1_ubuntu-latest: - name: instrumentation-grpc-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-grpc-1 -- -ra - - py312-test-instrumentation-grpc-0_ubuntu-latest: - name: instrumentation-grpc-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-grpc-0 -- -ra - - py312-test-instrumentation-grpc-1_ubuntu-latest: - name: instrumentation-grpc-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-grpc-1 -- -ra - - py313-test-instrumentation-grpc-1_ubuntu-latest: - name: instrumentation-grpc-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-grpc-1 -- -ra - - py314-test-instrumentation-grpc-1_ubuntu-latest: - name: instrumentation-grpc-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-grpc-1 -- -ra - - py39-test-instrumentation-sqlalchemy-1_ubuntu-latest: - name: instrumentation-sqlalchemy-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-sqlalchemy-1 -- -ra - - py39-test-instrumentation-sqlalchemy-2_ubuntu-latest: - name: instrumentation-sqlalchemy-2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-sqlalchemy-2 -- -ra - - py310-test-instrumentation-sqlalchemy-1_ubuntu-latest: - name: instrumentation-sqlalchemy-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-sqlalchemy-1 -- -ra - - py310-test-instrumentation-sqlalchemy-2_ubuntu-latest: - name: instrumentation-sqlalchemy-2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-sqlalchemy-2 -- -ra - - py311-test-instrumentation-sqlalchemy-1_ubuntu-latest: - name: instrumentation-sqlalchemy-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-sqlalchemy-1 -- -ra - - py311-test-instrumentation-sqlalchemy-2_ubuntu-latest: - name: instrumentation-sqlalchemy-2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-sqlalchemy-2 -- -ra - - py312-test-instrumentation-sqlalchemy-1_ubuntu-latest: - name: instrumentation-sqlalchemy-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-sqlalchemy-1 -- -ra - - py312-test-instrumentation-sqlalchemy-2_ubuntu-latest: - name: instrumentation-sqlalchemy-2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-sqlalchemy-2 -- -ra - - py313-test-instrumentation-sqlalchemy-1_ubuntu-latest: - name: instrumentation-sqlalchemy-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-sqlalchemy-1 -- -ra - - py313-test-instrumentation-sqlalchemy-2_ubuntu-latest: - name: instrumentation-sqlalchemy-2 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-sqlalchemy-2 -- -ra - - py314-test-instrumentation-sqlalchemy-2_ubuntu-latest: - name: instrumentation-sqlalchemy-2 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-sqlalchemy-2 -- -ra - - pypy3-test-instrumentation-sqlalchemy-0_ubuntu-latest: - name: instrumentation-sqlalchemy-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-sqlalchemy-0 -- -ra - - pypy3-test-instrumentation-sqlalchemy-1_ubuntu-latest: - name: instrumentation-sqlalchemy-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-sqlalchemy-1 -- -ra - - pypy3-test-instrumentation-sqlalchemy-2_ubuntu-latest: - name: instrumentation-sqlalchemy-2 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-sqlalchemy-2 -- -ra - - py39-test-instrumentation-redis_ubuntu-latest: - name: instrumentation-redis 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-redis -- -ra - - py310-test-instrumentation-redis_ubuntu-latest: - name: instrumentation-redis 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-redis -- -ra - - py311-test-instrumentation-redis_ubuntu-latest: - name: instrumentation-redis 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-redis -- -ra diff --git a/.github/workflows/test_2.yml b/.github/workflows/test_2.yml deleted file mode 100644 index bdc6cefdae..0000000000 --- a/.github/workflows/test_2.yml +++ /dev/null @@ -1,3643 +0,0 @@ -# Do not edit this file. -# This file is generated automatically by executing tox -e generate-workflows - -name: Test 2 - -on: - push: - branches-ignore: - - 'release/*' - - 'otelbot/*' - pull_request: - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -env: - # Set the SHA to the branch name if the PR has a label 'prepare-release' or 'backport' otherwise, set it to 'main' - # For PRs you can change the inner fallback ('main') - # For pushes you change the outer fallback ('main') - # The logic below is used during releases and depends on having an equivalent branch name in the core repo. - CORE_REPO_SHA: ${{ github.event_name == 'pull_request' && ( - contains(github.event.pull_request.labels.*.name, 'prepare-release') && github.event.pull_request.head.ref || - contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref || - 'main' - ) || 'main' }} - CONTRIB_REPO_SHA: main - PIP_EXISTS_ACTION: w - -jobs: - - py312-test-instrumentation-redis_ubuntu-latest: - name: instrumentation-redis 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-redis -- -ra - - py313-test-instrumentation-redis_ubuntu-latest: - name: instrumentation-redis 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-redis -- -ra - - py314-test-instrumentation-redis_ubuntu-latest: - name: instrumentation-redis 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-redis -- -ra - - pypy3-test-instrumentation-redis_ubuntu-latest: - name: instrumentation-redis pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-redis -- -ra - - py39-test-instrumentation-remoulade_ubuntu-latest: - name: instrumentation-remoulade 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-remoulade -- -ra - - py310-test-instrumentation-remoulade_ubuntu-latest: - name: instrumentation-remoulade 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-remoulade -- -ra - - py311-test-instrumentation-remoulade_ubuntu-latest: - name: instrumentation-remoulade 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-remoulade -- -ra - - py312-test-instrumentation-remoulade_ubuntu-latest: - name: instrumentation-remoulade 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-remoulade -- -ra - - py313-test-instrumentation-remoulade_ubuntu-latest: - name: instrumentation-remoulade 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-remoulade -- -ra - - py314-test-instrumentation-remoulade_ubuntu-latest: - name: instrumentation-remoulade 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-remoulade -- -ra - - py39-test-instrumentation-celery_ubuntu-latest: - name: instrumentation-celery 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-celery -- -ra - - py310-test-instrumentation-celery_ubuntu-latest: - name: instrumentation-celery 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-celery -- -ra - - py311-test-instrumentation-celery_ubuntu-latest: - name: instrumentation-celery 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-celery -- -ra - - py312-test-instrumentation-celery_ubuntu-latest: - name: instrumentation-celery 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-celery -- -ra - - py313-test-instrumentation-celery_ubuntu-latest: - name: instrumentation-celery 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-celery -- -ra - - py314-test-instrumentation-celery_ubuntu-latest: - name: instrumentation-celery 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-celery -- -ra - - pypy3-test-instrumentation-celery_ubuntu-latest: - name: instrumentation-celery pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-celery -- -ra - - py39-test-instrumentation-system-metrics_ubuntu-latest: - name: instrumentation-system-metrics 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-system-metrics -- -ra - - py310-test-instrumentation-system-metrics_ubuntu-latest: - name: instrumentation-system-metrics 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-system-metrics -- -ra - - py311-test-instrumentation-system-metrics_ubuntu-latest: - name: instrumentation-system-metrics 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-system-metrics -- -ra - - py312-test-instrumentation-system-metrics_ubuntu-latest: - name: instrumentation-system-metrics 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-system-metrics -- -ra - - py313-test-instrumentation-system-metrics_ubuntu-latest: - name: instrumentation-system-metrics 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-system-metrics -- -ra - - py314-test-instrumentation-system-metrics_ubuntu-latest: - name: instrumentation-system-metrics 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-system-metrics -- -ra - - pypy3-test-instrumentation-system-metrics_ubuntu-latest: - name: instrumentation-system-metrics pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-system-metrics -- -ra - - py39-test-instrumentation-threading_ubuntu-latest: - name: instrumentation-threading 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-threading -- -ra - - py310-test-instrumentation-threading_ubuntu-latest: - name: instrumentation-threading 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-threading -- -ra - - py311-test-instrumentation-threading_ubuntu-latest: - name: instrumentation-threading 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-threading -- -ra - - py312-test-instrumentation-threading_ubuntu-latest: - name: instrumentation-threading 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-threading -- -ra - - py313-test-instrumentation-threading_ubuntu-latest: - name: instrumentation-threading 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-threading -- -ra - - py314-test-instrumentation-threading_ubuntu-latest: - name: instrumentation-threading 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-threading -- -ra - - pypy3-test-instrumentation-threading_ubuntu-latest: - name: instrumentation-threading pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-threading -- -ra - - py39-test-instrumentation-tornado_ubuntu-latest: - name: instrumentation-tornado 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-tornado -- -ra - - py310-test-instrumentation-tornado_ubuntu-latest: - name: instrumentation-tornado 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-tornado -- -ra - - py311-test-instrumentation-tornado_ubuntu-latest: - name: instrumentation-tornado 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-tornado -- -ra - - py312-test-instrumentation-tornado_ubuntu-latest: - name: instrumentation-tornado 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-tornado -- -ra - - py313-test-instrumentation-tornado_ubuntu-latest: - name: instrumentation-tornado 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-tornado -- -ra - - py314-test-instrumentation-tornado_ubuntu-latest: - name: instrumentation-tornado 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-tornado -- -ra - - pypy3-test-instrumentation-tornado_ubuntu-latest: - name: instrumentation-tornado pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-tornado -- -ra - - py39-test-instrumentation-tortoiseorm_ubuntu-latest: - name: instrumentation-tortoiseorm 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-tortoiseorm -- -ra - - py310-test-instrumentation-tortoiseorm_ubuntu-latest: - name: instrumentation-tortoiseorm 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-tortoiseorm -- -ra - - py311-test-instrumentation-tortoiseorm_ubuntu-latest: - name: instrumentation-tortoiseorm 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-tortoiseorm -- -ra - - py312-test-instrumentation-tortoiseorm_ubuntu-latest: - name: instrumentation-tortoiseorm 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-tortoiseorm -- -ra - - py313-test-instrumentation-tortoiseorm_ubuntu-latest: - name: instrumentation-tortoiseorm 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-tortoiseorm -- -ra - - py314-test-instrumentation-tortoiseorm_ubuntu-latest: - name: instrumentation-tortoiseorm 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-tortoiseorm -- -ra - - pypy3-test-instrumentation-tortoiseorm_ubuntu-latest: - name: instrumentation-tortoiseorm pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-tortoiseorm -- -ra - - py39-test-instrumentation-httpx-0_ubuntu-latest: - name: instrumentation-httpx-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-httpx-0 -- -ra - - py39-test-instrumentation-httpx-1_ubuntu-latest: - name: instrumentation-httpx-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-httpx-1 -- -ra - - py310-test-instrumentation-httpx-0_ubuntu-latest: - name: instrumentation-httpx-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-httpx-0 -- -ra - - py310-test-instrumentation-httpx-1_ubuntu-latest: - name: instrumentation-httpx-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-httpx-1 -- -ra - - py311-test-instrumentation-httpx-0_ubuntu-latest: - name: instrumentation-httpx-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-httpx-0 -- -ra - - py311-test-instrumentation-httpx-1_ubuntu-latest: - name: instrumentation-httpx-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-httpx-1 -- -ra - - py312-test-instrumentation-httpx-0_ubuntu-latest: - name: instrumentation-httpx-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-httpx-0 -- -ra - - py312-test-instrumentation-httpx-1_ubuntu-latest: - name: instrumentation-httpx-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-httpx-1 -- -ra - - py313-test-instrumentation-httpx-1_ubuntu-latest: - name: instrumentation-httpx-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-httpx-1 -- -ra - - py314-test-instrumentation-httpx-1_ubuntu-latest: - name: instrumentation-httpx-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-httpx-1 -- -ra - - pypy3-test-instrumentation-httpx-0_ubuntu-latest: - name: instrumentation-httpx-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-httpx-0 -- -ra - - pypy3-test-instrumentation-httpx-1_ubuntu-latest: - name: instrumentation-httpx-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-httpx-1 -- -ra - - py39-test-util-http_ubuntu-latest: - name: util-http 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-util-http -- -ra - - py310-test-util-http_ubuntu-latest: - name: util-http 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-util-http -- -ra - - py311-test-util-http_ubuntu-latest: - name: util-http 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-util-http -- -ra - - py312-test-util-http_ubuntu-latest: - name: util-http 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-util-http -- -ra - - py313-test-util-http_ubuntu-latest: - name: util-http 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-util-http -- -ra - - py314-test-util-http_ubuntu-latest: - name: util-http 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-util-http -- -ra - - pypy3-test-util-http_ubuntu-latest: - name: util-http pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-util-http -- -ra - - py39-test-exporter-credential-provider-gcp_ubuntu-latest: - name: exporter-credential-provider-gcp 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-exporter-credential-provider-gcp -- -ra - - py310-test-exporter-credential-provider-gcp_ubuntu-latest: - name: exporter-credential-provider-gcp 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-exporter-credential-provider-gcp -- -ra - - py311-test-exporter-credential-provider-gcp_ubuntu-latest: - name: exporter-credential-provider-gcp 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-exporter-credential-provider-gcp -- -ra - - py312-test-exporter-credential-provider-gcp_ubuntu-latest: - name: exporter-credential-provider-gcp 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-exporter-credential-provider-gcp -- -ra - - py313-test-exporter-credential-provider-gcp_ubuntu-latest: - name: exporter-credential-provider-gcp 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-exporter-credential-provider-gcp -- -ra - - py314-test-exporter-credential-provider-gcp_ubuntu-latest: - name: exporter-credential-provider-gcp 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-exporter-credential-provider-gcp -- -ra - - py39-test-util-genai_ubuntu-latest: - name: util-genai 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-util-genai -- -ra - - py310-test-util-genai_ubuntu-latest: - name: util-genai 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-util-genai -- -ra - - py311-test-util-genai_ubuntu-latest: - name: util-genai 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-util-genai -- -ra - - py312-test-util-genai_ubuntu-latest: - name: util-genai 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-util-genai -- -ra - - py313-test-util-genai_ubuntu-latest: - name: util-genai 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-util-genai -- -ra - - py314-test-util-genai_ubuntu-latest: - name: util-genai 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-util-genai -- -ra - - pypy3-test-util-genai_ubuntu-latest: - name: util-genai pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-util-genai -- -ra - - py39-test-propagator-aws-xray-0_ubuntu-latest: - name: propagator-aws-xray-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-propagator-aws-xray-0 -- -ra - - py39-test-propagator-aws-xray-1_ubuntu-latest: - name: propagator-aws-xray-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-propagator-aws-xray-1 -- -ra - - py310-test-propagator-aws-xray-0_ubuntu-latest: - name: propagator-aws-xray-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-propagator-aws-xray-0 -- -ra - - py310-test-propagator-aws-xray-1_ubuntu-latest: - name: propagator-aws-xray-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-propagator-aws-xray-1 -- -ra - - py311-test-propagator-aws-xray-0_ubuntu-latest: - name: propagator-aws-xray-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-propagator-aws-xray-0 -- -ra - - py311-test-propagator-aws-xray-1_ubuntu-latest: - name: propagator-aws-xray-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-propagator-aws-xray-1 -- -ra - - py312-test-propagator-aws-xray-0_ubuntu-latest: - name: propagator-aws-xray-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-propagator-aws-xray-0 -- -ra - - py312-test-propagator-aws-xray-1_ubuntu-latest: - name: propagator-aws-xray-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-propagator-aws-xray-1 -- -ra - - py313-test-propagator-aws-xray-0_ubuntu-latest: - name: propagator-aws-xray-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-propagator-aws-xray-0 -- -ra - - py313-test-propagator-aws-xray-1_ubuntu-latest: - name: propagator-aws-xray-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-propagator-aws-xray-1 -- -ra - - py314-test-propagator-aws-xray-0_ubuntu-latest: - name: propagator-aws-xray-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-propagator-aws-xray-0 -- -ra - - py314-test-propagator-aws-xray-1_ubuntu-latest: - name: propagator-aws-xray-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-propagator-aws-xray-1 -- -ra - - pypy3-test-propagator-aws-xray-0_ubuntu-latest: - name: propagator-aws-xray-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-propagator-aws-xray-0 -- -ra - - pypy3-test-propagator-aws-xray-1_ubuntu-latest: - name: propagator-aws-xray-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-propagator-aws-xray-1 -- -ra - - py39-test-propagator-ot-trace_ubuntu-latest: - name: propagator-ot-trace 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-propagator-ot-trace -- -ra - - py310-test-propagator-ot-trace_ubuntu-latest: - name: propagator-ot-trace 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-propagator-ot-trace -- -ra - - py311-test-propagator-ot-trace_ubuntu-latest: - name: propagator-ot-trace 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-propagator-ot-trace -- -ra - - py312-test-propagator-ot-trace_ubuntu-latest: - name: propagator-ot-trace 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-propagator-ot-trace -- -ra - - py313-test-propagator-ot-trace_ubuntu-latest: - name: propagator-ot-trace 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-propagator-ot-trace -- -ra - - py314-test-propagator-ot-trace_ubuntu-latest: - name: propagator-ot-trace 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-propagator-ot-trace -- -ra - - pypy3-test-propagator-ot-trace_ubuntu-latest: - name: propagator-ot-trace pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-propagator-ot-trace -- -ra - - py39-test-instrumentation-sio-pika-0_ubuntu-latest: - name: instrumentation-sio-pika-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-sio-pika-0 -- -ra - - py39-test-instrumentation-sio-pika-1_ubuntu-latest: - name: instrumentation-sio-pika-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-sio-pika-1 -- -ra - - py310-test-instrumentation-sio-pika-0_ubuntu-latest: - name: instrumentation-sio-pika-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-sio-pika-0 -- -ra - - py310-test-instrumentation-sio-pika-1_ubuntu-latest: - name: instrumentation-sio-pika-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-sio-pika-1 -- -ra - - py311-test-instrumentation-sio-pika-0_ubuntu-latest: - name: instrumentation-sio-pika-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-sio-pika-0 -- -ra - - py311-test-instrumentation-sio-pika-1_ubuntu-latest: - name: instrumentation-sio-pika-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-sio-pika-1 -- -ra - - py312-test-instrumentation-sio-pika-0_ubuntu-latest: - name: instrumentation-sio-pika-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-sio-pika-0 -- -ra - - py312-test-instrumentation-sio-pika-1_ubuntu-latest: - name: instrumentation-sio-pika-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-sio-pika-1 -- -ra - - py313-test-instrumentation-sio-pika-0_ubuntu-latest: - name: instrumentation-sio-pika-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-sio-pika-0 -- -ra - - py313-test-instrumentation-sio-pika-1_ubuntu-latest: - name: instrumentation-sio-pika-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-sio-pika-1 -- -ra - - py314-test-instrumentation-sio-pika-0_ubuntu-latest: - name: instrumentation-sio-pika-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-sio-pika-0 -- -ra - - py314-test-instrumentation-sio-pika-1_ubuntu-latest: - name: instrumentation-sio-pika-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-sio-pika-1 -- -ra - - pypy3-test-instrumentation-sio-pika-0_ubuntu-latest: - name: instrumentation-sio-pika-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-sio-pika-0 -- -ra - - pypy3-test-instrumentation-sio-pika-1_ubuntu-latest: - name: instrumentation-sio-pika-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-sio-pika-1 -- -ra - - py39-test-instrumentation-aio-pika-0_ubuntu-latest: - name: instrumentation-aio-pika-0 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-0 -- -ra - - py39-test-instrumentation-aio-pika-1_ubuntu-latest: - name: instrumentation-aio-pika-1 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-1 -- -ra - - py39-test-instrumentation-aio-pika-2_ubuntu-latest: - name: instrumentation-aio-pika-2 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-2 -- -ra - - py39-test-instrumentation-aio-pika-3_ubuntu-latest: - name: instrumentation-aio-pika-3 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aio-pika-3 -- -ra - - py310-test-instrumentation-aio-pika-0_ubuntu-latest: - name: instrumentation-aio-pika-0 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aio-pika-0 -- -ra - - py310-test-instrumentation-aio-pika-1_ubuntu-latest: - name: instrumentation-aio-pika-1 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aio-pika-1 -- -ra - - py310-test-instrumentation-aio-pika-2_ubuntu-latest: - name: instrumentation-aio-pika-2 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aio-pika-2 -- -ra - - py310-test-instrumentation-aio-pika-3_ubuntu-latest: - name: instrumentation-aio-pika-3 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aio-pika-3 -- -ra - - py311-test-instrumentation-aio-pika-0_ubuntu-latest: - name: instrumentation-aio-pika-0 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aio-pika-0 -- -ra - - py311-test-instrumentation-aio-pika-1_ubuntu-latest: - name: instrumentation-aio-pika-1 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aio-pika-1 -- -ra - - py311-test-instrumentation-aio-pika-2_ubuntu-latest: - name: instrumentation-aio-pika-2 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aio-pika-2 -- -ra - - py311-test-instrumentation-aio-pika-3_ubuntu-latest: - name: instrumentation-aio-pika-3 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aio-pika-3 -- -ra - - py312-test-instrumentation-aio-pika-0_ubuntu-latest: - name: instrumentation-aio-pika-0 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aio-pika-0 -- -ra - - py312-test-instrumentation-aio-pika-1_ubuntu-latest: - name: instrumentation-aio-pika-1 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aio-pika-1 -- -ra - - py312-test-instrumentation-aio-pika-2_ubuntu-latest: - name: instrumentation-aio-pika-2 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aio-pika-2 -- -ra - - py312-test-instrumentation-aio-pika-3_ubuntu-latest: - name: instrumentation-aio-pika-3 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aio-pika-3 -- -ra - - py313-test-instrumentation-aio-pika-0_ubuntu-latest: - name: instrumentation-aio-pika-0 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aio-pika-0 -- -ra - - py313-test-instrumentation-aio-pika-1_ubuntu-latest: - name: instrumentation-aio-pika-1 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aio-pika-1 -- -ra - - py313-test-instrumentation-aio-pika-2_ubuntu-latest: - name: instrumentation-aio-pika-2 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aio-pika-2 -- -ra - - py313-test-instrumentation-aio-pika-3_ubuntu-latest: - name: instrumentation-aio-pika-3 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aio-pika-3 -- -ra - - py314-test-instrumentation-aio-pika-0_ubuntu-latest: - name: instrumentation-aio-pika-0 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aio-pika-0 -- -ra - - py314-test-instrumentation-aio-pika-1_ubuntu-latest: - name: instrumentation-aio-pika-1 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aio-pika-1 -- -ra - - py314-test-instrumentation-aio-pika-2_ubuntu-latest: - name: instrumentation-aio-pika-2 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aio-pika-2 -- -ra - - py314-test-instrumentation-aio-pika-3_ubuntu-latest: - name: instrumentation-aio-pika-3 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aio-pika-3 -- -ra - - pypy3-test-instrumentation-aio-pika-0_ubuntu-latest: - name: instrumentation-aio-pika-0 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aio-pika-0 -- -ra - - pypy3-test-instrumentation-aio-pika-1_ubuntu-latest: - name: instrumentation-aio-pika-1 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aio-pika-1 -- -ra - - pypy3-test-instrumentation-aio-pika-2_ubuntu-latest: - name: instrumentation-aio-pika-2 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aio-pika-2 -- -ra - - pypy3-test-instrumentation-aio-pika-3_ubuntu-latest: - name: instrumentation-aio-pika-3 pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aio-pika-3 -- -ra - - py39-test-instrumentation-aiokafka_ubuntu-latest: - name: instrumentation-aiokafka 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-aiokafka -- -ra - - py310-test-instrumentation-aiokafka_ubuntu-latest: - name: instrumentation-aiokafka 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-aiokafka -- -ra - - py311-test-instrumentation-aiokafka_ubuntu-latest: - name: instrumentation-aiokafka 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-aiokafka -- -ra - - py312-test-instrumentation-aiokafka_ubuntu-latest: - name: instrumentation-aiokafka 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-aiokafka -- -ra - - py313-test-instrumentation-aiokafka_ubuntu-latest: - name: instrumentation-aiokafka 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-aiokafka -- -ra - - py314-test-instrumentation-aiokafka_ubuntu-latest: - name: instrumentation-aiokafka 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-aiokafka -- -ra - - pypy3-test-instrumentation-aiokafka_ubuntu-latest: - name: instrumentation-aiokafka pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-aiokafka -- -ra - - py39-test-instrumentation-kafka-python_ubuntu-latest: - name: instrumentation-kafka-python 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-kafka-python -- -ra - - py310-test-instrumentation-kafka-python_ubuntu-latest: - name: instrumentation-kafka-python 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-kafka-python -- -ra - - py311-test-instrumentation-kafka-python_ubuntu-latest: - name: instrumentation-kafka-python 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-kafka-python -- -ra - - py39-test-instrumentation-kafka-pythonng_ubuntu-latest: - name: instrumentation-kafka-pythonng 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-kafka-pythonng -- -ra - - py310-test-instrumentation-kafka-pythonng_ubuntu-latest: - name: instrumentation-kafka-pythonng 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-kafka-pythonng -- -ra - - py311-test-instrumentation-kafka-pythonng_ubuntu-latest: - name: instrumentation-kafka-pythonng 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-kafka-pythonng -- -ra - - py312-test-instrumentation-kafka-pythonng_ubuntu-latest: - name: instrumentation-kafka-pythonng 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-kafka-pythonng -- -ra - - py313-test-instrumentation-kafka-pythonng_ubuntu-latest: - name: instrumentation-kafka-pythonng 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-kafka-pythonng -- -ra - - py314-test-instrumentation-kafka-pythonng_ubuntu-latest: - name: instrumentation-kafka-pythonng 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-kafka-pythonng -- -ra - - pypy3-test-instrumentation-kafka-python_ubuntu-latest: - name: instrumentation-kafka-python pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-kafka-python -- -ra - - pypy3-test-instrumentation-kafka-pythonng_ubuntu-latest: - name: instrumentation-kafka-pythonng pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-kafka-pythonng -- -ra - - py39-test-instrumentation-confluent-kafka_ubuntu-latest: - name: instrumentation-confluent-kafka 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-confluent-kafka -- -ra - - py310-test-instrumentation-confluent-kafka_ubuntu-latest: - name: instrumentation-confluent-kafka 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-confluent-kafka -- -ra - - py311-test-instrumentation-confluent-kafka_ubuntu-latest: - name: instrumentation-confluent-kafka 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-confluent-kafka -- -ra - - py312-test-instrumentation-confluent-kafka_ubuntu-latest: - name: instrumentation-confluent-kafka 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-confluent-kafka -- -ra - - py313-test-instrumentation-confluent-kafka_ubuntu-latest: - name: instrumentation-confluent-kafka 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-confluent-kafka -- -ra - - py314-test-instrumentation-confluent-kafka_ubuntu-latest: - name: instrumentation-confluent-kafka 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-confluent-kafka -- -ra - - py39-test-instrumentation-asyncio_ubuntu-latest: - name: instrumentation-asyncio 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-asyncio -- -ra - - py310-test-instrumentation-asyncio_ubuntu-latest: - name: instrumentation-asyncio 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-asyncio -- -ra - - py311-test-instrumentation-asyncio_ubuntu-latest: - name: instrumentation-asyncio 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-asyncio -- -ra - - py312-test-instrumentation-asyncio_ubuntu-latest: - name: instrumentation-asyncio 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-asyncio -- -ra - - py313-test-instrumentation-asyncio_ubuntu-latest: - name: instrumentation-asyncio 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-asyncio -- -ra - - py314-test-instrumentation-asyncio_ubuntu-latest: - name: instrumentation-asyncio 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-asyncio -- -ra - - py39-test-instrumentation-cassandra-driver_ubuntu-latest: - name: instrumentation-cassandra-driver 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-cassandra-driver -- -ra - - py39-test-instrumentation-cassandra-scylla_ubuntu-latest: - name: instrumentation-cassandra-scylla 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-instrumentation-cassandra-scylla -- -ra - - py310-test-instrumentation-cassandra-driver_ubuntu-latest: - name: instrumentation-cassandra-driver 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-cassandra-driver -- -ra - - py310-test-instrumentation-cassandra-scylla_ubuntu-latest: - name: instrumentation-cassandra-scylla 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-instrumentation-cassandra-scylla -- -ra - - py311-test-instrumentation-cassandra-driver_ubuntu-latest: - name: instrumentation-cassandra-driver 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-cassandra-driver -- -ra - - py311-test-instrumentation-cassandra-scylla_ubuntu-latest: - name: instrumentation-cassandra-scylla 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-instrumentation-cassandra-scylla -- -ra - - py312-test-instrumentation-cassandra-driver_ubuntu-latest: - name: instrumentation-cassandra-driver 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-cassandra-driver -- -ra - - py312-test-instrumentation-cassandra-scylla_ubuntu-latest: - name: instrumentation-cassandra-scylla 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-instrumentation-cassandra-scylla -- -ra - - py313-test-instrumentation-cassandra-driver_ubuntu-latest: - name: instrumentation-cassandra-driver 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-cassandra-driver -- -ra - - py313-test-instrumentation-cassandra-scylla_ubuntu-latest: - name: instrumentation-cassandra-scylla 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-instrumentation-cassandra-scylla -- -ra - - py314-test-instrumentation-cassandra-driver_ubuntu-latest: - name: instrumentation-cassandra-driver 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-cassandra-driver -- -ra - - py314-test-instrumentation-cassandra-scylla_ubuntu-latest: - name: instrumentation-cassandra-scylla 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-instrumentation-cassandra-scylla -- -ra - - pypy3-test-instrumentation-cassandra-scylla_ubuntu-latest: - name: instrumentation-cassandra-scylla pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-instrumentation-cassandra-scylla -- -ra - - py39-test-processor-baggage_ubuntu-latest: - name: processor-baggage 3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: "3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py39-test-processor-baggage -- -ra - - py310-test-processor-baggage_ubuntu-latest: - name: processor-baggage 3.10 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py310-test-processor-baggage -- -ra - - py311-test-processor-baggage_ubuntu-latest: - name: processor-baggage 3.11 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py311-test-processor-baggage -- -ra - - py312-test-processor-baggage_ubuntu-latest: - name: processor-baggage 3.12 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py312-test-processor-baggage -- -ra - - py313-test-processor-baggage_ubuntu-latest: - name: processor-baggage 3.13 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py313-test-processor-baggage -- -ra - - py314-test-processor-baggage_ubuntu-latest: - name: processor-baggage 3.14 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python 3.14 - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e py314-test-processor-baggage -- -ra - - pypy3-test-processor-baggage_ubuntu-latest: - name: processor-baggage pypy-3.9 Ubuntu - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repo @ SHA - ${{ github.sha }} - uses: actions/checkout@v4 - - - name: Set up Python pypy-3.9 - uses: actions/setup-python@v5 - with: - python-version: "pypy-3.9" - - - name: Install tox - run: pip install tox-uv - - - name: Run tests - run: tox -e pypy3-test-processor-baggage -- -ra diff --git a/.gitignore b/.gitignore index 1c32b4446a..6c3e08c117 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,8 @@ target # Benchmark result files *-benchmark.json + +# opentelemetry-admin jobs +opentelemetry-admin-jobs.txt + +.claude/settings.local.json diff --git a/.pylintrc b/.pylintrc index e51f6f43bd..d3a4723112 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,7 +7,7 @@ extension-pkg-whitelist=cassandra # Add list of files or directories to be excluded. They should be base names, not # paths. -ignore=CVS,gen,Dockerfile,docker-compose.yml,README.md,requirements.txt,docs,.venv,site-packages,.tox +ignore=CVS,gen,Dockerfile,docker-compose.yml,README.md,requirements.txt,docs,.venv,site-packages,.tox,proto # Add files or directories matching the regex patterns to be excluded. The # regex matches against base names, not paths. @@ -37,16 +37,12 @@ persistent=yes # Specify a configuration file. #rcfile= -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no # Run python dependant checks considering the baseline version -py-version=3.9 +py-version=3.10 [MESSAGES CONTROL] @@ -76,11 +72,11 @@ disable=missing-docstring, exec-used, super-with-arguments, # temp-pylint-upgrade isinstance-second-argument-not-valid-type, # temp-pylint-upgrade - raise-missing-from, # temp-pylint-upgrade - unused-argument, # temp-pylint-upgrade - protected-access, # temp-pylint-upgrade + raise-missing-from, # temp-pylint-upgrade + unused-argument, # temp-pylint-upgrade + protected-access, # temp-pylint-upgrade super-init-not-called, # temp-pylint-upgrade - invalid-overridden-method, # temp-pylint-upgrade + invalid-overridden-method, # temp-pylint-upgrade missing-module-docstring, # temp-pylint-upgrade import-error, # needed as a workaround as reported here: https://github.com/open-telemetry/opentelemetry-python-contrib/issues/290 cyclic-import, @@ -179,7 +175,7 @@ contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. -generated-members=types_pb2.* +generated-members=types_pb2.*,anyvalue_pb2.*,opamp_pb2.* # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). @@ -463,6 +459,9 @@ valid-metaclass-classmethod-first-arg=cls # Maximum number of arguments for function / method. max-args=5 +# Maximum number of positional arguments for function / method. +max-positional-arguments=12 + # Maximum number of attributes for a class (see R0902). max-attributes=7 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..7467033607 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,69 @@ +# OpenTelemetry Python Contrib + +This file is here to steer AI assisted PRs towards being high quality and valuable contributions +that do not create excessive maintainer burden. + +Monorepo with 50+ OpenTelemetry instrumentation packages for Python. + +## General Rules and Guidelines + +The most important rule is not to post comments on issues or PRs that are AI-generated. Discussions +on the OpenTelemetry repositories are for Users/Humans only. + +Follow the PR scoping guidance in [CONTRIBUTING.md](CONTRIBUTING.md). Keep AI-assisted PRs tightly +isolated to the requested change and never include unrelated cleanup or opportunistic improvements +unless they are strictly necessary for correctness. + +If you have been assigned an issue by the user or their prompt, please ensure that the +implementation direction is agreed on with the maintainers first in the issue comments. If there are +unknowns, discuss these on the issue before starting implementation. Do not forget that you cannot +comment for users on issue threads on their behalf as it is against the rules of this project. + +## Structure + +- `instrumentation/` - instrumentation packages (Flask, Django, FastAPI, gRPC, databases, etc.) +- `instrumentation-genai/` - GenAI instrumentations (Anthropic, Vertex AI, LangChain, etc.) +- `util/` - shared utilities (`util-http`, `util-genai`) +- `exporter/` - custom exporters +- `propagator/` - context propagators + +Instrumentation packages live under `src/opentelemetry/instrumentation/{name}/` with their own +`pyproject.toml` and `tests/`. Other package types follow the equivalent layout under their own +namespace (e.g. `src/opentelemetry/util/{name}/`, `src/opentelemetry/exporter/{name}/`). + +## Commands + +```sh +# Install all packages and dev tools +uv sync --frozen --all-packages + +# Lint (runs ruff via pre-commit) +uv run pre-commit run ruff --all-files + +# Test a specific package (append -0, -1, etc. for version variants) +uv run tox -e py312-test-instrumentation-flask-0 + +# Type check +uv run tox -e typecheck +``` + +## Guidelines + +- Each package has its own `pyproject.toml` with version, dependencies, and entry points. +- The monorepo uses `uv` workspaces. +- `tox.ini` defines the test matrix - check it for available test environments. +- Do not add `type: ignore` comments. If a type error arises, solve it properly or write a follow-up plan to address it in another PR. +- Whenever applicable, all code changes should have tests that actually validate the changes. + +## Commit formatting + +We appreciate it if users disclose the use of AI tools when the significant part of a commit is +taken from a tool without changes. When making a commit this should be disclosed through an +`Assisted-by:` commit message trailer. + +Examples: + +``` +Assisted-by: ChatGPT 5.2 +Assisted-by: Claude Opus 4.6 +``` diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e4827020..8818f8b070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,61 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Bump `pylint` to `4.0.5` + ([#4244](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4244)) + +### Breaking changes + +- Drop Python 3.9 support + ([#4412](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4412)) + +## Version 1.41.0/0.62b0 (2026-04-09) + +### Added + +- `opentelemetry-instrumentation-asgi`: Respect `suppress_http_instrumentation` context in ASGI middleware to skip server span creation when HTTP instrumentation is suppressed + ([#4375](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4375)) +- `opentelemetry-instrumentation-confluent-kafka`: Loosen confluent-kafka upper bound to <3.0.0 + ([#4289](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4289)) +- `opentelemetry-instrumentation`: Add support for wrapt 2.x + ([#4203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4203)) +- `opentelemetry-instrumentation-psycopg2`: Add parameter `capture_parameters` to instrumentor. + ([#4212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4212)) +- `opentelemetry-instrumentation-botocore`: Add support for instrumenting `aiobotocore` + ([#4049](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4049)) +- `opentelemetry-instrumentation-sqlalchemy`: implement new semantic convention opt-in migration + ([#4110](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4110)) + +### Fixed + +- `opentelemetry-docker-tests`: Replace deprecated `SpanAttributes` from `opentelemetry.semconv.trace` with `opentelemetry.semconv._incubating.attributes` + ([#4339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4339)) +- `opentelemetry-instrumentation-confluent-kafka`: Skip `recv` span creation when `poll()` returns no message or `consume()` returns an empty list, avoiding empty spans on idle polls + ([#4349](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4349)) +- Fix intermittent `Core Contrib Test` CI failures caused by GitHub git CDN SHA propagation lag by installing core packages from the already-checked-out local copy instead of a second git clone + ([#4305](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4305)) +- Don't import module in unwrap if not already imported + ([#4321](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4321)) +- `opentelemetry-instrumentation-logging`: Map Python `CRITICAL` log level to OTel `FATAL` severity text and `WARNING` to `WARN` + ([#4365](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4365)) +- `opentelemetry-instrumentation-logging`: Add recursion guard in LoggingHandler.emit to prevent deadlock + ([#4302](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4302)) +- `opentelemetry-instrumentation-grpc`: Fix bidirectional streaming RPCs raising `AttributeError: 'generator' object has no attribute 'add_done_callback'` + ([#4259](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4259)) +- `opentelemetry-instrumentation-aiokafka`: fix `Unclosed AIOKafkaProducer` warning and `RuntimeWarning: coroutine was never awaited` in tests + ([#4384](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4384)) +- `opentelemetry-instrumentation-aiokafka`: Fix compatibility with aiokafka 0.13 by calling + `_key_serializer`/`_value_serializer` directly instead of the internal `_serialize` method + whose signature changed in 0.13 from `(topic, key, value)` to `(key, value, headers)` + ([#4379](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4379)) + +### Breaking changes + +- `opentelemetry-instrumentation-boto`: Remove instrumentation + ([#4303](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4303)) + ## Version 1.40.0/0.61b0 (2026-03-04) ### Added diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..43c994c2d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/README.md b/README.md index 8de305d091..fee84f5bba 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,8 @@ license
- - Build Status 0 - - - Build Status 1 + + Build Status Beta

@@ -127,7 +124,8 @@ For more information about the maintainer role, see the [community repository](h - [Jeremy Voss](https://github.com/jeremydvoss), Microsoft - [Keith Decker](https://github.com/keith-decker), Cisco/Splunk - [Liudmila Molkova](https://github.com/lmolkova), Grafana Labs -- [Lukas Hering](https://github.com/herin049), Capital One +- [Lukas Hering](https://github.com/herin049), Oracle +- [Mike Goldsmith](https://github.com/MikeGoldsmith), Honeycomb - [Owais Lone](https://github.com/owais), Splunk - [Pablo Collins](https://github.com/pmcollins), Splunk - [Sanket Mehta](https://github.com/sanketmehta28), Cisco @@ -136,22 +134,17 @@ For more information about the maintainer role, see the [community repository](h For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver). -### Emeritus Maintainers - -- [Alex Boten](https://github.com/codeboten) -- [Diego Hurtado](https://github.com/ocelotl) -- [Owais Lone](https://github.com/owais) -- [Shalev Roda](https://github.com/shalevr) -- [Yusuke Tsutsumi](https://github.com/toumorokoshi) - -For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager). - -### Emeritus Approvers - -- [Ashutosh Goel](https://github.com/ashu658) -- [Nathaniel Ruiz Nowell](https://github.com/NathanielRN) -- [Nikolay Sokolik](https://github.com/nikosokolik) -- [Nikolay Sokolik](https://github.com/oxeye-nikolay) +### Emeritus + +- [Alex Boten](https://github.com/codeboten), Maintainer +- [Ashutosh Goel](https://github.com/ashu658), Approver +- [Diego Hurtado](https://github.com/ocelotl), Maintainer +- [Nathaniel Ruiz Nowell](https://github.com/NathanielRN), Approver +- [Nikolay Sokolik](https://github.com/nikosokolik), Approver +- [Nikolay Sokolik](https://github.com/oxeye-nikolay), Approver +- [Owais Lone](https://github.com/owais), Maintainer +- [Shalev Roda](https://github.com/shalevr), Maintainer +- [Yusuke Tsutsumi](https://github.com/toumorokoshi), Maintainer For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager). diff --git a/RELEASING.md b/RELEASING.md index 95cb24b4ec..6213a6e442 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -18,6 +18,7 @@ > [!NOTE] > Per-package release is supported for the following packages only: +> - opentelemetry-opamp-client > - opentelemetry-propagator-aws-xray > - opentelemetry-resource-detector-azure > - opentelemetry-sdk-extension-aws @@ -87,6 +88,7 @@ The workflow will create a pull request that should be merged in order to procee > [!NOTE] > Per-package patch release is supported for the following packages only: +> - opentelemetry-opamp-client > - opentelemetry-propagator-aws-xray > - opentelemetry-resource-detector-azure > - opentelemetry-sdk-extension-aws diff --git a/_template/pyproject.toml b/_template/pyproject.toml index c9f3ba3ed1..c579035d5f 100644 --- a/_template/pyproject.toml +++ b/_template/pyproject.toml @@ -12,7 +12,7 @@ dynamic = ["version"] description = "" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -22,7 +22,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/_template/version.py b/_template/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/_template/version.py +++ b/_template/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/dev-requirements.txt b/dev-requirements.txt index b2de8cecfc..28b7f81487 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,4 @@ -pylint==3.0.2 +pylint==4.0.5 httpretty==1.1.4 pyright==v1.1.404 sphinx==7.1.2 @@ -13,6 +13,5 @@ codespell==2.1.0 requests==2.32.3 ruamel.yaml==0.17.21 flaky==3.7.0 -pre-commit==3.7.0; python_version >= '3.9' -pre-commit==3.5.0; python_version < '3.9' +pre-commit==3.7.0 ruff==0.14.1 diff --git a/docs-requirements.txt b/docs-requirements.txt index 9d5e42fe74..8f743798bc 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -17,17 +17,16 @@ fsspec>=2025.9.0 # Required by instrumentation and exporter packages aio_pika~=7.2.0 -aiohttp~=3.0 +aiohttp~=3.13 aiokafka~=0.11.0 aiopg>=0.13.0,<1.3.0 asyncpg>=0.12.0 asyncclick~=8.0 -boto~=2.0 botocore~=1.0 boto3~=1.0 cassandra-driver~=3.25 celery>=4.0 -confluent-kafka>= 1.8.2,<= 2.13.0 +confluent-kafka>= 1.8.2,< 3.0.0 elasticsearch>=6.0,<9.0 flask~=2.0 falcon~=2.0 @@ -51,6 +50,11 @@ starlette~=0.50 tornado>=5.1.1 tortoise-orm>=0.17.0 +# required by opamp +uuid_utils +protobuf>=5.0,< 7.0 + # indirect dependency pins markupsafe==2.0.1 itsdangerous==2.0.1 + diff --git a/docs/conf.py b/docs/conf.py index 0f42c92be7..df62d5c15a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,21 +28,21 @@ exp = "../exporter" exp_dirs = [ - os.path.abspath("/".join(["../exporter", f, "src"])) + os.path.abspath("/".join([exp, f, "src"])) for f in listdir(exp) if isdir(join(exp, f)) ] instr = "../instrumentation" instr_dirs = [ - os.path.abspath("/".join(["../instrumentation", f, "src"])) + os.path.abspath("/".join([instr, f, "src"])) for f in listdir(instr) if isdir(join(instr, f)) ] instr_genai = "../instrumentation-genai" instr_genai_dirs = [ - os.path.abspath("/".join(["../instrumentation-genai", f, "src"])) + os.path.abspath("/".join([instr_genai, f, "src"])) for f in listdir(instr_genai) if isdir(join(instr_genai, f)) ] @@ -56,23 +56,32 @@ sdk_ext = "../sdk-extension" sdk_ext_dirs = [ - os.path.abspath("/".join(["../sdk-extension", f, "src"])) + os.path.abspath("/".join([sdk_ext, f, "src"])) for f in listdir(sdk_ext) if isdir(join(sdk_ext, f)) ] resource = "../resource" resource_dirs = [ - os.path.abspath("/".join(["../resource", f, "src"])) + os.path.abspath("/".join([resource, f, "src"])) for f in listdir(resource) if isdir(join(resource, f)) ] + util = "../util" util_dirs = [ os.path.abspath("/".join([util, f, "src"])) for f in listdir(util) if isdir(join(util, f)) ] + +opamp = "../opamp" +opamp_dirs = [ + os.path.abspath("/".join([opamp, f, "src"])) + for f in listdir(opamp) + if isdir(join(opamp, f)) +] + sys.path[:0] = ( exp_dirs + instr_dirs @@ -81,6 +90,7 @@ + prop_dirs + resource_dirs + util_dirs + + opamp_dirs ) # -- Project information ----------------------------------------------------- @@ -124,7 +134,7 @@ "https://opentracing-python.readthedocs.io/en/latest/", None, ), - "aiohttp": ("https://aiohttp.readthedocs.io/en/stable/", None), + "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), "wrapt": ("https://wrapt.readthedocs.io/en/latest/", None), "pymongo": ("https://pymongo.readthedocs.io/en/stable/", None), "opentelemetry": ( @@ -141,7 +151,20 @@ # Sphinx does not recognize generic type TypeVars # Container supposedly were fixed, but does not work # https://github.com/sphinx-doc/sphinx/pull/3744 -nitpick_ignore = [] +nitpick_ignore = [ + ( + "py:class", + "opamp_pb2.RemoteConfigStatus", + ), + ( + "py:class", + "opamp_pb2.EffectiveConfig", + ), + ( + "py:class", + "opamp_pb2.AgentRemoteConfig", + ), +] cfg = ConfigParser() cfg.read("./nitpick-exceptions.ini") diff --git a/docs/index.rst b/docs/index.rst index 11c611685b..1ac7f8179c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -103,6 +103,14 @@ install resource/** +.. toctree:: + :maxdepth: 2 + :caption: OpAMP + :name: OpAMP + :glob: + + opamp/** + Indices and tables ------------------ diff --git a/docs/instrumentation/boto/boto.rst b/docs/instrumentation/boto/boto.rst deleted file mode 100644 index c438c2466c..0000000000 --- a/docs/instrumentation/boto/boto.rst +++ /dev/null @@ -1,7 +0,0 @@ -OpenTelemetry Boto Instrumentation -================================== - -.. automodule:: opentelemetry.instrumentation.boto - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/opamp/client.rst b/docs/opamp/client.rst new file mode 100644 index 0000000000..74ababd2db --- /dev/null +++ b/docs/opamp/client.rst @@ -0,0 +1,7 @@ +OpenTelemetry Python - OpAMP Client +=================================== + +.. automodule:: opentelemetry._opamp + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/performance/benchmarks.rst b/docs/performance/benchmarks.rst index 99859a27d8..9ef78c099d 100644 --- a/docs/performance/benchmarks.rst +++ b/docs/performance/benchmarks.rst @@ -1,4 +1,8 @@ Performance Tests - Benchmarks ============================== -Click `here `_ to view the latest performance benchmarks for packages in this repo. +For instructions on running and writing benchmarks locally, see the +`Benchmarks section of CONTRIBUTING.md `_. + +Live benchmark results are published at +`opentelemetry.io/docs/languages/python/benchmarks `_. diff --git a/eachdist.ini b/eachdist.ini index 14b4b00fca..1304a41134 100644 --- a/eachdist.ini +++ b/eachdist.ini @@ -16,7 +16,7 @@ sortfirst= ext/* [stable] -version=1.41.0.dev +version=1.42.0.dev packages= opentelemetry-sdk @@ -35,7 +35,7 @@ packages= opentelemetry-exporter-credential-provider-gcp [prerelease] -version=0.62b0.dev +version=0.63b0.dev packages= all @@ -48,6 +48,8 @@ packages= [exclude_release] packages= + opentelemetry-opamp-client + opentelemetry-propagator-aws-xray opentelemetry-resource-detector-azure opentelemetry-sdk-extension-aws opentelemetry-propagator-aws-xray diff --git a/exporter/opentelemetry-exporter-credential-provider-gcp/pyproject.toml b/exporter/opentelemetry-exporter-credential-provider-gcp/pyproject.toml index 5eb6b7096b..d53c8e4a0a 100644 --- a/exporter/opentelemetry-exporter-credential-provider-gcp/pyproject.toml +++ b/exporter/opentelemetry-exporter-credential-provider-gcp/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "GCP OTLP Exporter Credential Provider for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/exporter/opentelemetry-exporter-credential-provider-gcp/src/opentelemetry/gcp_credential_provider/version.py b/exporter/opentelemetry-exporter-credential-provider-gcp/src/opentelemetry/gcp_credential_provider/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/exporter/opentelemetry-exporter-credential-provider-gcp/src/opentelemetry/gcp_credential_provider/version.py +++ b/exporter/opentelemetry-exporter-credential-provider-gcp/src/opentelemetry/gcp_credential_provider/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/example/Dockerfile b/exporter/opentelemetry-exporter-prometheus-remote-write/example/Dockerfile index 8b2d34d421..4bde0b14bb 100644 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/example/Dockerfile +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/example/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9 +FROM python:3.10 RUN apt-get update -y && apt-get install libsnappy-dev -y diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/pyproject.toml b/exporter/opentelemetry-exporter-prometheus-remote-write/pyproject.toml index 40db4fc255..285d0b22c6 100644 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/pyproject.toml +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/pyproject.toml @@ -9,7 +9,7 @@ dynamic = ["version"] description = "Prometheus Remote Write Metrics Exporter for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -19,7 +19,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/version.py b/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/version.py +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/test-requirements.txt b/exporter/opentelemetry-exporter-prometheus-remote-write/test-requirements.txt index 129f0589a7..bb03ea1236 100644 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/test-requirements.txt +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/test-requirements.txt @@ -6,7 +6,7 @@ Deprecated==1.2.14 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 protobuf==6.33.5 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/exporter/opentelemetry-exporter-richconsole/pyproject.toml b/exporter/opentelemetry-exporter-richconsole/pyproject.toml index 57c16a81d0..805e28340a 100644 --- a/exporter/opentelemetry-exporter-richconsole/pyproject.toml +++ b/exporter/opentelemetry-exporter-richconsole/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Rich Console Exporter for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -28,7 +27,7 @@ classifiers = [ dependencies = [ "opentelemetry-api ~= 1.12", "opentelemetry-sdk ~= 1.12", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", "rich>=10.0.0", ] diff --git a/exporter/opentelemetry-exporter-richconsole/src/opentelemetry/exporter/richconsole/version.py b/exporter/opentelemetry-exporter-richconsole/src/opentelemetry/exporter/richconsole/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/exporter/opentelemetry-exporter-richconsole/src/opentelemetry/exporter/richconsole/version.py +++ b/exporter/opentelemetry-exporter-richconsole/src/opentelemetry/exporter/richconsole/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/exporter/opentelemetry-exporter-richconsole/test-requirements.txt b/exporter/opentelemetry-exporter-richconsole/test-requirements.txt index 40d37f1d66..d00e32aa9e 100644 --- a/exporter/opentelemetry-exporter-richconsole/test-requirements.txt +++ b/exporter/opentelemetry-exporter-richconsole/test-requirements.txt @@ -5,7 +5,7 @@ iniconfig==2.0.0 markdown-it-py==3.0.0 mdurl==0.1.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 Pygments==2.17.2 pytest==7.4.4 diff --git a/instrumentation-genai/AGENTS.md b/instrumentation-genai/AGENTS.md new file mode 100644 index 0000000000..6e8dd1eca9 --- /dev/null +++ b/instrumentation-genai/AGENTS.md @@ -0,0 +1,41 @@ +# GenAI Instrumentation — Agent and Contributor Guidelines + +Instrumentation packages here wrap specific libraries (OpenAI, Anthropic, etc.) and bridge +them to the shared telemetry layer in `util/opentelemetry-util-genai`. + +## 1. Instrumentation Layer Boundary + +Do not call OpenTelemetry APIs (`tracer`, `meter`, `span`, event APIs) directly. +Always go through `TelemetryHandler` and the invocation objects it returns. + +This layer is responsible only for: + +- Patching the library +- Parsing library-specific input/output into invocation fields + +Everything else (span creation, metric recording, event emission, context propagation) +belongs in `util/opentelemetry-util-genai`. + +## 2. Invocation Pattern + +Use `start_*()` and control span lifetime manually: + +```python +invocation = handler.start_inference(provider, request_model, server_address=..., server_port=...) +invocation.temperature = ... +try: + response = client.call(...) + invocation.response_model_name = response.model + invocation.finish_reasons = response.finish_reasons + invocation.stop() +except Exception as exc: + invocation.fail(exc) + raise +``` + +## 3. Exception Handling + +- Do not add `raise {Error}` statements in instrumentation/telemetry code — validation belongs in + tests and callers, not in the instrumentation layer. +- When catching exceptions from the underlying library to record telemetry, always re-raise + the original exception unmodified. diff --git a/instrumentation-genai/CLAUDE.md b/instrumentation-genai/CLAUDE.md new file mode 120000 index 0000000000..47dc3e3d86 --- /dev/null +++ b/instrumentation-genai/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/CHANGELOG.md b/instrumentation-genai/opentelemetry-instrumentation-anthropic/CHANGELOG.md index ba164c0ebc..dfa9e01631 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/CHANGELOG.md +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/CHANGELOG.md @@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add async Anthropic message stream wrappers and manager wrappers, with wrapper + tests ([#4346](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4346)) + - `AsyncMessagesStreamWrapper` for async message stream telemetry + - `AsyncMessagesStreamManagerWrapper` for async `Messages.stream()` telemetry +- Add sync streaming support for `Messages.create(stream=True)` and `Messages.stream()` + ([#4155](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4155)) + - `StreamWrapper` for handling `Messages.create(stream=True)` telemetry + - `MessageStreamManagerWrapper` for handling `Messages.stream()` telemetry + - `MessageWrapper` for non-streaming response telemetry extraction - Initial implementation of Anthropic instrumentation ([#3978](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3978)) - Implement sync `Messages.create` instrumentation with GenAI semantic convention attributes @@ -17,4 +26,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Captures response attributes: `gen_ai.response.id`, `gen_ai.response.model`, `gen_ai.response.finish_reasons`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` - Error handling with `error.type` attribute - Minimum supported anthropic version is 0.16.0 (SDK uses modern `anthropic.resources.messages` module structure introduced in this version) - diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/pyproject.toml index 32642eaf2b..6b0d72c334 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Anthropic instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,16 +25,14 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-api ~= 1.37", - "opentelemetry-instrumentation ~= 0.58b0", - "opentelemetry-semantic-conventions ~= 0.58b0", + "opentelemetry-api ~= 1.39", + "opentelemetry-instrumentation ~= 0.60b0", + "opentelemetry-semantic-conventions ~= 0.60b0", "opentelemetry-util-genai >= 0.2b0, <0.4b0", ] [project.optional-dependencies] -instruments = [ - "anthropic >= 0.16.0", -] +instruments = ["anthropic >= 0.51.0"] [project.entry-points.opentelemetry_instrumentor] anthropic = "opentelemetry.instrumentation.anthropic:AnthropicInstrumentor" @@ -48,15 +45,10 @@ Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib" path = "src/opentelemetry/instrumentation/anthropic/version.py" [tool.hatch.build.targets.sdist] -include = [ - "/src", - "/tests", - "/examples", -] +include = ["/src", "/tests", "/examples"] [tool.hatch.build.targets.wheel] packages = ["src/opentelemetry"] [tool.pytest.ini_options] testpaths = ["tests"] - diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/__init__.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/__init__.py index bf76798462..f5286438a4 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/__init__.py +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/__init__.py @@ -54,7 +54,9 @@ ) from opentelemetry.instrumentation.anthropic.package import _instruments -from opentelemetry.instrumentation.anthropic.patch import messages_create +from opentelemetry.instrumentation.anthropic.patch import ( + messages_create, +) from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import unwrap from opentelemetry.util.genai.handler import TelemetryHandler @@ -89,11 +91,12 @@ def _instrument(self, **kwargs: Any) -> None: # Get providers from kwargs tracer_provider = kwargs.get("tracer_provider") meter_provider = kwargs.get("meter_provider") + logger_provider = kwargs.get("logger_provider") - # TODO: Add logger_provider to TelemetryHandler to capture content events. handler = TelemetryHandler( tracer_provider=tracer_provider, meter_provider=meter_provider, + logger_provider=logger_provider, ) # Patch Messages.create diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/messages_extractors.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/messages_extractors.py new file mode 100644 index 0000000000..8c3dcf26ec --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/messages_extractors.py @@ -0,0 +1,251 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Get/extract helpers for Anthropic Messages instrumentation.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Sequence + +from anthropic.types import MessageDeltaUsage + +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAIAttributes, +) +from opentelemetry.semconv._incubating.attributes import ( + server_attributes as ServerAttributes, +) +from opentelemetry.util.genai.types import ( + InputMessage, + LLMInvocation, + MessagePart, + OutputMessage, +) +from opentelemetry.util.types import AttributeValue + +from .utils import ( + convert_content_to_parts, + normalize_finish_reason, +) + +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + + import httpx + from anthropic.resources.messages import Messages + from anthropic.types import ( + Message, + MessageParam, + MetadataParam, + TextBlockParam, + ThinkingConfigParam, + ToolChoiceParam, + ToolUnionParam, + Usage, + ) + + +@dataclass +class MessageRequestParams: + model: str | None = None + max_tokens: int | None = None + temperature: float | None = None + top_k: int | None = None + top_p: float | None = None + stop_sequences: Sequence[str] | None = None + stream: bool | None = None + messages: Iterable[MessageParam] | None = None + system: str | Iterable[TextBlockParam] | None = None + + +GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = ( + "gen_ai.usage.cache_creation.input_tokens" +) +GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens" + + +@dataclass +class UsageTokens: + input_tokens: int | None = None + output_tokens: int | None = None + cache_creation_input_tokens: int | None = None + cache_read_input_tokens: int | None = None + + +def extract_usage_tokens( + usage: Usage | MessageDeltaUsage | None, +) -> UsageTokens: + if usage is None: + return UsageTokens() + + input_tokens = usage.input_tokens + output_tokens = usage.output_tokens + cache_creation_input_tokens = usage.cache_creation_input_tokens + cache_read_input_tokens = usage.cache_read_input_tokens + + if ( + input_tokens is None + and cache_creation_input_tokens is None + and cache_read_input_tokens is None + ): + total_input_tokens = None + else: + total_input_tokens = ( + (input_tokens or 0) + + (cache_creation_input_tokens or 0) + + (cache_read_input_tokens or 0) + ) + + return UsageTokens( + input_tokens=total_input_tokens, + output_tokens=output_tokens, + cache_creation_input_tokens=cache_creation_input_tokens, + cache_read_input_tokens=cache_read_input_tokens, + ) + + +def get_input_messages( + messages: Iterable[MessageParam] | None, +) -> list[InputMessage]: + if messages is None: + return [] + result: list[InputMessage] = [] + for message in messages: + role = message["role"] + parts = convert_content_to_parts(message["content"]) + result.append(InputMessage(role=role, parts=parts)) + return result + + +def get_system_instruction( + system: str | Iterable[TextBlockParam] | None, +) -> list[MessagePart]: + if system is None: + return [] + return convert_content_to_parts(system) + + +def get_output_messages_from_message( + message: Message | None, +) -> list[OutputMessage]: + if message is None: + return [] + + parts = convert_content_to_parts(message.content) + finish_reason = normalize_finish_reason(message.stop_reason) + return [ + OutputMessage( + role=message.role, + parts=parts, + finish_reason=finish_reason or "", + ) + ] + + +def set_invocation_response_attributes( + invocation: LLMInvocation, + message: Message | None, + capture_content: bool, +) -> None: + if message is None: + return + + invocation.response_model_name = message.model + invocation.response_id = message.id + + finish_reason = normalize_finish_reason(message.stop_reason) + if finish_reason: + invocation.finish_reasons = [finish_reason] + + tokens = extract_usage_tokens(message.usage) + invocation.input_tokens = tokens.input_tokens + invocation.output_tokens = tokens.output_tokens + if tokens.cache_creation_input_tokens is not None: + invocation.attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS] = ( + tokens.cache_creation_input_tokens + ) + if tokens.cache_read_input_tokens is not None: + invocation.attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = ( + tokens.cache_read_input_tokens + ) + + if capture_content: + invocation.output_messages = get_output_messages_from_message(message) + + +def extract_params( # pylint: disable=too-many-locals + *, + max_tokens: int | None = None, + messages: Iterable[MessageParam] | None = None, + model: str | None = None, + metadata: MetadataParam | None = None, + service_tier: str | None = None, + stop_sequences: Sequence[str] | None = None, + stream: bool | None = None, + system: str | Iterable[TextBlockParam] | None = None, + temperature: float | None = None, + thinking: ThinkingConfigParam | None = None, + tool_choice: ToolChoiceParam | None = None, + tools: Iterable[ToolUnionParam] | None = None, + top_k: int | None = None, + top_p: float | None = None, + extra_headers: Mapping[str, str] | None = None, + extra_query: Mapping[str, object] | None = None, + extra_body: object | None = None, + timeout: float | httpx.Timeout | None = None, + **_kwargs: object, +) -> MessageRequestParams: + return MessageRequestParams( + model=model, + max_tokens=max_tokens, + temperature=temperature, + top_p=top_p, + top_k=top_k, + stop_sequences=stop_sequences, + stream=stream, + messages=messages, + system=system, + ) + + +def _set_server_address_and_port( + client_instance: "Messages", + attributes: dict[str, AttributeValue | None], +) -> None: + base_url = client_instance._client.base_url + host = base_url.host + if host: + attributes[ServerAttributes.SERVER_ADDRESS] = host + + port = base_url.port + if port and port != 443 and port > 0: + attributes[ServerAttributes.SERVER_PORT] = port + + +def get_llm_request_attributes( + params: MessageRequestParams, client_instance: "Messages" +) -> dict[str, AttributeValue]: + attributes: dict[str, AttributeValue | None] = { + GenAIAttributes.GEN_AI_OPERATION_NAME: GenAIAttributes.GenAiOperationNameValues.CHAT.value, + GenAIAttributes.GEN_AI_SYSTEM: GenAIAttributes.GenAiSystemValues.ANTHROPIC.value, # pyright: ignore[reportDeprecated] + GenAIAttributes.GEN_AI_REQUEST_MODEL: params.model, + GenAIAttributes.GEN_AI_REQUEST_MAX_TOKENS: params.max_tokens, + GenAIAttributes.GEN_AI_REQUEST_TEMPERATURE: params.temperature, + GenAIAttributes.GEN_AI_REQUEST_TOP_P: params.top_p, + GenAIAttributes.GEN_AI_REQUEST_TOP_K: params.top_k, + GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES: params.stop_sequences, + } + _set_server_address_and_port(client_instance, attributes) + return {k: v for k, v in attributes.items() if v is not None} diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/patch.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/patch.py index 0562d638e2..845e10de3f 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/patch.py +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/patch.py @@ -14,65 +14,118 @@ """Patching functions for Anthropic instrumentation.""" -from typing import TYPE_CHECKING, Any, Callable +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING, Any, Callable, Union, cast + +from anthropic._streaming import Stream as AnthropicStream +from anthropic.types import Message as AnthropicMessage from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAIAttributes, ) from opentelemetry.util.genai.handler import TelemetryHandler -from opentelemetry.util.genai.types import LLMInvocation +from opentelemetry.util.genai.types import ( + Error, + LLMInvocation, # TODO: migrate to InferenceInvocation +) +from opentelemetry.util.genai.utils import ( + should_capture_content_on_spans_in_experimental_mode, +) -from .utils import ( +from .messages_extractors import ( extract_params, + get_input_messages, get_llm_request_attributes, + get_system_instruction, +) +from .wrappers import ( + MessagesStreamWrapper, + MessageWrapper, ) if TYPE_CHECKING: from anthropic.resources.messages import Messages - from anthropic.types import Message + from anthropic.types import RawMessageStreamEvent + + +_logger = logging.getLogger(__name__) +ANTHROPIC = "anthropic" def messages_create( handler: TelemetryHandler, -) -> Callable[..., "Message"]: +) -> Callable[ + ..., + Union[ + "AnthropicMessage", + "AnthropicStream[RawMessageStreamEvent]", + MessagesStreamWrapper[None], + ], +]: """Wrap the `create` method of the `Messages` class to trace it.""" + capture_content = should_capture_content_on_spans_in_experimental_mode() def traced_method( - wrapped: Callable[..., "Message"], + wrapped: Callable[ + ..., + Union[ + "AnthropicMessage", + "AnthropicStream[RawMessageStreamEvent]", + ], + ], instance: "Messages", args: tuple[Any, ...], kwargs: dict[str, Any], - ) -> "Message": + ) -> Union[ + "AnthropicMessage", + "AnthropicStream[RawMessageStreamEvent]", + MessagesStreamWrapper[None], + ]: params = extract_params(*args, **kwargs) attributes = get_llm_request_attributes(params, instance) - request_model = str( - attributes.get(GenAIAttributes.GEN_AI_REQUEST_MODEL) - or params.model - or "unknown" + request_model_attribute = attributes.get( + GenAIAttributes.GEN_AI_REQUEST_MODEL + ) + request_model = ( + request_model_attribute + if isinstance(request_model_attribute, str) + else params.model ) invocation = LLMInvocation( request_model=request_model, - provider="anthropic", + provider=ANTHROPIC, + input_messages=get_input_messages(params.messages) + if capture_content + else [], + system_instruction=get_system_instruction(params.system) + if capture_content + else [], attributes=attributes, ) - with handler.llm(invocation) as invocation: + # Use manual lifecycle management for both streaming and non-streaming + handler.start_llm(invocation) + try: result = wrapped(*args, **kwargs) - - if result.model: - invocation.response_model_name = result.model - - if result.id: - invocation.response_id = result.id - - if result.stop_reason: - invocation.finish_reasons = [result.stop_reason] - - if result.usage: - invocation.input_tokens = result.usage.input_tokens - invocation.output_tokens = result.usage.output_tokens - - return result - - return traced_method + if isinstance(result, AnthropicStream): + return MessagesStreamWrapper( + result, handler, invocation, capture_content + ) + + wrapper = MessageWrapper(result, capture_content) + wrapper.extract_into(invocation) + handler.stop_llm(invocation) + return wrapper.message + except Exception as exc: + handler.fail_llm( + invocation, Error(message=str(exc), type=type(exc)) + ) + raise + + return cast( + 'Callable[..., Union["AnthropicMessage", "AnthropicStream[RawMessageStreamEvent]", MessagesStreamWrapper[None]]]', + traced_method, + ) diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/utils.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/utils.py index 4c2003f441..7399b8831b 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/utils.py +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/utils.py @@ -12,134 +12,232 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Utility functions for Anthropic instrumentation.""" +"""Shared helper utilities for Anthropic instrumentation.""" from __future__ import annotations +import base64 +import json from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Optional, Sequence -from urllib.parse import urlparse - -from opentelemetry.semconv._incubating.attributes import ( - gen_ai_attributes as GenAIAttributes, +from typing import TYPE_CHECKING, Any + +from anthropic.types import ( + InputJSONDelta, + RedactedThinkingBlock, + ServerToolUseBlock, + TextBlock, + TextDelta, + ThinkingBlock, + ThinkingDelta, + ToolUseBlock, + WebSearchToolResultBlock, ) -from opentelemetry.semconv._incubating.attributes import ( - server_attributes as ServerAttributes, + +from opentelemetry.util.genai.types import ( + Blob, + MessagePart, + Reasoning, + Text, + ToolCallRequest, + ToolCallResponse, ) -from opentelemetry.util.types import AttributeValue if TYPE_CHECKING: - from anthropic.resources.messages import Messages + from collections.abc import Iterable, Mapping + + from anthropic.types import ( + ContentBlock, + ContentBlockParam, + RawContentBlockDelta, + ) @dataclass -class MessageCreateParams: - """Parameters extracted from Messages.create() call.""" - - model: str | None = None - max_tokens: int | None = None - temperature: float | None = None - top_p: float | None = None - top_k: int | None = None - stop_sequences: Sequence[str] | None = None - - -# Use parameter signature from -# https://github.com/anthropics/anthropic-sdk-python/blob/9b5ab24ba17bcd5e762e5a5fd69bb3c17b100aaa/src/anthropic/resources/messages/messages.py#L92 -# to handle named vs positional args robustly -def extract_params( # pylint: disable=too-many-locals - *, - max_tokens: int | None = None, - messages: Any | None = None, - model: str | None = None, - metadata: Any | None = None, - service_tier: Any | None = None, - stop_sequences: Sequence[str] | None = None, - stream: Any | None = None, - system: Any | None = None, - temperature: float | None = None, - thinking: Any | None = None, - tool_choice: Any | None = None, - tools: Any | None = None, - top_p: float | None = None, - top_k: int | None = None, - extra_headers: Any | None = None, - extra_query: Any | None = None, - extra_body: Any | None = None, - timeout: Any | None = None, - **_kwargs: Any, -) -> MessageCreateParams: - """Extract relevant parameters from Messages.create() arguments.""" - return MessageCreateParams( - model=model, - max_tokens=max_tokens, - temperature=temperature, - top_p=top_p, - top_k=top_k, - stop_sequences=stop_sequences, +class StreamBlockState: + type: str + text: str = "" + tool_id: str | None = None + tool_name: str = "" + tool_input: dict[str, object] | None = None + input_json: str = "" + thinking: str = "" + + +def normalize_finish_reason(stop_reason: str | None) -> str | None: + if stop_reason is None: + return None + normalized = { + "end_turn": "stop", + "stop_sequence": "stop", + "max_tokens": "length", + "tool_use": "tool_calls", + }.get(stop_reason) + return normalized or stop_reason + + +def _decode_base64(data: str) -> bytes | None: + try: + return base64.b64decode(data) + except Exception: # pylint: disable=broad-exception-caught + return None + + +def _extract_base64_blob(source: object, modality: str) -> Blob | None: + """Extract a Blob from a base64-encoded source dict.""" + if not isinstance(source, dict): + return None + # source is a TypedDict (e.g. Base64ImageSourceParam) narrowed to dict; + # pyright cannot infer value types from isinstance-narrowed dicts. + data: object = source.get("data") # type: ignore[reportUnknownMemberType] + if not isinstance(data, str): + return None + decoded = _decode_base64(data) + if decoded is None: + return None + media_type: object = source.get("media_type") # type: ignore[reportUnknownMemberType] + return Blob( + mime_type=media_type if isinstance(media_type, str) else None, + modality=modality, + content=decoded, ) -def set_server_address_and_port( - client_instance: "Messages", attributes: dict[str, Any] +def _convert_dict_block_to_part( + block: Mapping[str, Any], +) -> MessagePart | None: + """Convert a request-param content block (TypedDict/dict) to a MessagePart.""" + block_type = block.get("type") + + if block_type == "text": + text = block.get("text") + return Text(content=str(text) if text is not None else "") + + if block_type == "tool_use": + inp = block.get("input") + return ToolCallRequest( + arguments=inp if isinstance(inp, dict) else None, + name=str(block.get("name", "")), + id=str(block.get("id", "")), + ) + + if block_type == "tool_result": + return ToolCallResponse( + response=block.get("content"), + id=str(block.get("tool_use_id", "")), + ) + + if block_type in ("thinking", "redacted_thinking"): + thinking = block.get("thinking") or block.get("data") + return Reasoning(content=str(thinking) if thinking is not None else "") + + if block_type in ("image", "audio", "video", "document", "file"): + return _extract_base64_blob(block.get("source"), str(block_type)) + + return None + + +def _convert_content_block_to_part( + block: ContentBlock | ContentBlockParam, +) -> MessagePart | None: + """Convert an Anthropic content block to a MessagePart.""" + if isinstance(block, TextBlock): + return Text(content=block.text) + + if isinstance(block, (ToolUseBlock, ServerToolUseBlock)): + return ToolCallRequest( + arguments=block.input, name=block.name, id=block.id + ) + + if isinstance(block, (ThinkingBlock, RedactedThinkingBlock)): + content = ( + block.thinking if isinstance(block, ThinkingBlock) else block.data + ) + return Reasoning(content=content) + + if isinstance(block, WebSearchToolResultBlock): + return ToolCallResponse( + response=block.model_dump().get("content"), + id=block.tool_use_id, + ) + + # ContentBlockParam variants are TypedDicts (dicts at runtime); + # newer SDK versions may add Pydantic block types not handled above. + if isinstance(block, dict): + return _convert_dict_block_to_part(block) + + return None + + +def convert_content_to_parts( + content: str | Iterable[ContentBlock | ContentBlockParam] | None, +) -> list[MessagePart]: + if content is None: + return [] + if isinstance(content, str): + return [Text(content=content)] + parts: list[MessagePart] = [] + for item in content: + part = _convert_content_block_to_part(item) + if part is not None: + parts.append(part) + return parts + + +def create_stream_block_state(content_block: ContentBlock) -> StreamBlockState: + if isinstance(content_block, TextBlock): + return StreamBlockState(type="text", text=content_block.text) + + if isinstance(content_block, (ToolUseBlock, ServerToolUseBlock)): + return StreamBlockState( + type="tool_use", + tool_id=content_block.id, + tool_name=content_block.name, + tool_input=content_block.input, + ) + + if isinstance(content_block, ThinkingBlock): + return StreamBlockState( + type="thinking", thinking=content_block.thinking + ) + + if isinstance(content_block, RedactedThinkingBlock): + return StreamBlockState(type="redacted_thinking") + + return StreamBlockState(type=content_block.type) + + +def update_stream_block_state( + state: StreamBlockState, delta: RawContentBlockDelta ) -> None: - """Extract server address and port from the Anthropic client instance.""" - base_client = getattr(client_instance, "_client", None) - base_url = getattr(base_client, "base_url", None) - if not base_url: - return - - port: Optional[int] = None - if hasattr(base_url, "host"): - # httpx.URL object - attributes[ServerAttributes.SERVER_ADDRESS] = base_url.host - port = getattr(base_url, "port", None) - elif isinstance(base_url, str): - url = urlparse(base_url) - attributes[ServerAttributes.SERVER_ADDRESS] = url.hostname - port = url.port - - if port and port != 443 and port > 0: - attributes[ServerAttributes.SERVER_PORT] = port - - -def get_llm_request_attributes( - params: MessageCreateParams, client_instance: "Messages" -) -> dict[str, AttributeValue]: - """Extract LLM request attributes from MessageCreateParams. - - Returns a dictionary of OpenTelemetry semantic convention attributes for LLM requests. - The attributes follow the GenAI semantic conventions (gen_ai.*) and server semantic - conventions (server.*) as defined in the OpenTelemetry specification. - - GenAI attributes included: - - gen_ai.operation.name: The operation name (e.g., "chat") - - gen_ai.system: The GenAI system identifier (e.g., "anthropic") - - gen_ai.request.model: The model identifier - - gen_ai.request.max_tokens: Maximum tokens in the request - - gen_ai.request.temperature: Sampling temperature - - gen_ai.request.top_p: Top-p sampling parameter - - gen_ai.request.top_k: Top-k sampling parameter - - gen_ai.request.stop_sequences: Stop sequences for the request - - Server attributes included (if available): - - server.address: The server hostname - - server.port: The server port (if not default 443) - - Only non-None values are included in the returned dictionary. - """ - attributes = { - GenAIAttributes.GEN_AI_OPERATION_NAME: GenAIAttributes.GenAiOperationNameValues.CHAT.value, - GenAIAttributes.GEN_AI_SYSTEM: GenAIAttributes.GenAiSystemValues.ANTHROPIC.value, # pyright: ignore[reportDeprecated] - GenAIAttributes.GEN_AI_REQUEST_MODEL: params.model, - GenAIAttributes.GEN_AI_REQUEST_MAX_TOKENS: params.max_tokens, - GenAIAttributes.GEN_AI_REQUEST_TEMPERATURE: params.temperature, - GenAIAttributes.GEN_AI_REQUEST_TOP_P: params.top_p, - GenAIAttributes.GEN_AI_REQUEST_TOP_K: params.top_k, - GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES: params.stop_sequences, - } - - set_server_address_and_port(client_instance, attributes) - - # Filter out None values - return {k: v for k, v in attributes.items() if v is not None} + if isinstance(delta, TextDelta): + state.type = "text" + state.text += delta.text + elif isinstance(delta, InputJSONDelta): + state.type = "tool_use" + state.input_json += delta.partial_json + elif isinstance(delta, ThinkingDelta): + state.type = "thinking" + state.thinking += delta.thinking + + +def stream_block_state_to_part(state: StreamBlockState) -> MessagePart | None: + if state.type == "text": + return Text(content=state.text) + + if state.type == "tool_use": + arguments: str | dict[str, object] | None = state.tool_input + if state.input_json: + try: + arguments = json.loads(state.input_json) + except ValueError: + arguments = state.input_json + return ToolCallRequest( + arguments=arguments, + name=state.tool_name, + id=state.tool_id, + ) + + if state.type in ("thinking", "redacted_thinking"): + return Reasoning(content=state.thinking) + + return None diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/wrappers.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/wrappers.py new file mode 100644 index 0000000000..1a9d5319a5 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/wrappers.py @@ -0,0 +1,435 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import logging +from contextlib import AsyncExitStack, ExitStack, contextmanager +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Generator, + Generic, + Iterator, + TypeVar, + cast, +) + +from opentelemetry.util.genai.handler import TelemetryHandler +from opentelemetry.util.genai.types import ( + Error, + LLMInvocation, # TODO: migrate to InferenceInvocation +) + +from .messages_extractors import set_invocation_response_attributes + +try: + from anthropic.lib.streaming._messages import ( # pylint: disable=no-name-in-module + accumulate_event as _sdk_accumulate_event, + ) +except ImportError: + _sdk_accumulate_event = None + +if TYPE_CHECKING: + from anthropic._streaming import AsyncStream, Stream + from anthropic.lib.streaming._messages import ( # pylint: disable=no-name-in-module + AsyncMessageStream, + AsyncMessageStreamManager, + MessageStream, + MessageStreamManager, + ) + from anthropic.lib.streaming._types import ( # pylint: disable=no-name-in-module + ParsedMessageStreamEvent, + ) + from anthropic.types import ( + Message, + RawMessageStreamEvent, + ) + from anthropic.types.parsed_message import ParsedMessage + + +_logger = logging.getLogger(__name__) +ResponseT = TypeVar("ResponseT") +ResponseFormatT = TypeVar("ResponseFormatT") +accumulate_event = cast("Callable[..., Message] | None", _sdk_accumulate_event) + + +def _set_response_attributes( + invocation: LLMInvocation, + result: "Message | None", + capture_content: bool, +) -> None: + set_invocation_response_attributes(invocation, result, capture_content) + + +class _ResponseProxy(Generic[ResponseT]): + def __init__(self, response: ResponseT, finalize: Callable[[], None]): + self._response: Any = response + self._finalize = finalize + + def close(self) -> None: + try: + self._response.close() + finally: + self._finalize() + + def __getattr__(self, name: str): + return getattr(self._response, name) + + +class _AsyncResponseProxy(Generic[ResponseT]): + def __init__(self, response: ResponseT, finalize: Callable[[], None]): + self._response: Any = response + self._finalize = finalize + + async def aclose(self) -> None: + try: + await self._response.aclose() + finally: + self._finalize() + + def __getattr__(self, name: str): + return getattr(self._response, name) + + +class MessageWrapper: + """Wrapper for non-streaming Message response that handles telemetry.""" + + def __init__(self, message: Message, capture_content: bool): + self._message = message + self._capture_content = capture_content + + def extract_into(self, invocation: LLMInvocation) -> None: + """Extract response data into the invocation.""" + set_invocation_response_attributes( + invocation, self._message, self._capture_content + ) + + @property + def message(self) -> Message: + """Return the wrapped Message object.""" + return self._message + + +class MessagesStreamWrapper( + Generic[ResponseFormatT], + Iterator[ + "RawMessageStreamEvent | ParsedMessageStreamEvent[ResponseFormatT]" + ], +): + """Wrapper for Anthropic Stream that handles telemetry.""" + + def __init__( + self, + stream: "Stream[RawMessageStreamEvent] | MessageStream[ResponseFormatT]", + handler: TelemetryHandler, + invocation: LLMInvocation, + capture_content: bool, + ): + self.stream = stream + self.handler = handler + self.invocation = invocation + self._message: "Message | ParsedMessage[ResponseFormatT] | None" = None + self._capture_content = capture_content + self._finalized = False + + def __enter__(self) -> "MessagesStreamWrapper[ResponseFormatT]": + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + if exc_type is not None: + self._fail( + str(exc_val), type(exc_val) if exc_val else Exception + ) + finally: + self.close() + return False + + def close(self) -> None: + try: + self.stream.close() + finally: + self._stop() + + def __iter__(self) -> "MessagesStreamWrapper[ResponseFormatT]": + return self + + def __next__( + self, + ) -> "RawMessageStreamEvent | ParsedMessageStreamEvent[ResponseFormatT]": + try: + chunk = next(self.stream) + except StopIteration: + self._stop() + raise + except Exception as exc: + self._fail(str(exc), type(exc)) + raise + with self._safe_instrumentation("stream chunk processing"): + self._process_chunk(chunk) + return chunk + + def __getattr__(self, name: str) -> object: + return getattr(self.stream, name) + + @property + def response(self): + return _ResponseProxy(self.stream.response, self._stop) + + def _stop(self) -> None: + if self._finalized: + return + with self._safe_instrumentation("response attribute extraction"): + _set_response_attributes( + self.invocation, self._message, self._capture_content + ) + with self._safe_instrumentation("stop_llm"): + self.handler.stop_llm(self.invocation) + self._finalized = True + + def _fail(self, message: str, error_type: type[BaseException]) -> None: + if self._finalized: + return + with self._safe_instrumentation("fail_llm"): + self.handler.fail_llm( + self.invocation, Error(message=message, type=error_type) + ) + self._finalized = True + + @staticmethod + @contextmanager + def _safe_instrumentation( + context: str, + ) -> Generator[None, None, None]: + try: + yield + except Exception: # pylint: disable=broad-exception-caught + _logger.debug( + "Anthropic MessagesStreamWrapper instrumentation error in %s", + context, + exc_info=True, + ) + + def _process_chunk( + self, + chunk: "RawMessageStreamEvent | ParsedMessageStreamEvent[ResponseFormatT]", + ) -> None: + """Accumulate a final message snapshot from a streaming chunk.""" + snapshot = cast( + "ParsedMessage[ResponseFormatT] | None", + getattr(self.stream, "current_message_snapshot", None), + ) + if snapshot is not None: + self._message = snapshot + return + if accumulate_event is None: + return + self._message = accumulate_event( + event=cast("RawMessageStreamEvent", chunk), + current_snapshot=cast( + "ParsedMessage[ResponseFormatT] | None", self._message + ), + ) + + +class AsyncMessagesStreamWrapper(MessagesStreamWrapper[ResponseFormatT]): + """Wrapper for async Anthropic Stream that handles telemetry.""" + + def __init__( + self, + stream: "AsyncStream[RawMessageStreamEvent] | AsyncMessageStream[ResponseFormatT]", + handler: TelemetryHandler, + invocation: LLMInvocation, + capture_content: bool, + ): + self.stream = stream + self.handler = handler + self.invocation = invocation + self._message: "Message | ParsedMessage[ResponseFormatT] | None" = None + self._capture_content = capture_content + self._finalized = False + + async def __aenter__( + self, + ) -> "AsyncMessagesStreamWrapper[ResponseFormatT]": + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + if exc_type is not None: + self._fail( + str(exc_val), type(exc_val) if exc_val else Exception + ) + finally: + await self.close() + return False + + async def close(self) -> None: # type: ignore[override] + try: + await self.stream.close() + finally: + self._stop() + + def __aiter__(self) -> "AsyncMessagesStreamWrapper[ResponseFormatT]": + return self + + @property + def response(self) -> Any: + return _AsyncResponseProxy(self.stream.response, self._stop) + + async def __anext__( + self, + ) -> "RawMessageStreamEvent | ParsedMessageStreamEvent[ResponseFormatT]": + try: + chunk = await self.stream.__anext__() + except StopAsyncIteration: + self._stop() + raise + except Exception as exc: + self._fail(str(exc), type(exc)) + raise + with self._safe_instrumentation("stream chunk processing"): + self._process_chunk(chunk) + return chunk + + +class MessagesStreamManagerWrapper(Generic[ResponseFormatT]): + """Wrapper for sync Anthropic stream managers.""" + + def __init__( + self, + manager: "MessageStreamManager[ResponseFormatT]", + handler: TelemetryHandler, + invocation: LLMInvocation, + capture_content: bool, + ): + self._manager = manager + self._handler = handler + self._invocation = invocation + self._capture_content = capture_content + self._stream_wrapper: MessagesStreamWrapper[ResponseFormatT] | None = ( + None + ) + + def __enter__(self) -> MessagesStreamWrapper[ResponseFormatT]: + stream = self._manager.__enter__() + self._stream_wrapper = MessagesStreamWrapper( + stream, + self._handler, + self._invocation, + self._capture_content, + ) + return self._stream_wrapper + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + suppressed = False + stream_wrapper = self._stream_wrapper + self._stream_wrapper = None + with ExitStack() as cleanup: + if stream_wrapper is not None: + + def finalize_stream_wrapper() -> None: + if suppressed: + stream_wrapper.__exit__(None, None, None) + else: + stream_wrapper.__exit__(exc_type, exc_val, exc_tb) + + cleanup.callback(finalize_stream_wrapper) + suppressed = self._manager.__exit__(exc_type, exc_val, exc_tb) + return suppressed + + def __getattr__(self, name: str) -> object: + return getattr(self._manager, name) + + +class AsyncMessagesStreamManagerWrapper(Generic[ResponseFormatT]): + """Wrapper for AsyncMessageStreamManager that handles telemetry. + + Wraps AsyncMessageStreamManager from the Anthropic SDK: + https://github.com/anthropics/anthropic-sdk-python/blob/05220bc1c1079fe01f5c4babc007ec7a990859d9/src/anthropic/lib/streaming/_messages.py#L294 + """ + + def __init__( + self, + manager: "AsyncMessageStreamManager[ResponseFormatT]", + handler: TelemetryHandler, + invocation: LLMInvocation, + capture_content: bool, + ): + self._manager = manager + self._handler = handler + self._invocation = invocation + self._capture_content = capture_content + self._stream_wrapper: ( + AsyncMessagesStreamWrapper[ResponseFormatT] | None + ) = None + + async def __aenter__( + self, + ) -> AsyncMessagesStreamWrapper[ResponseFormatT]: + msg_stream = await self._manager.__aenter__() + self._stream_wrapper = AsyncMessagesStreamWrapper( + msg_stream, + self._handler, + self._invocation, + self._capture_content, + ) + return self._stream_wrapper + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + suppressed = False + stream_wrapper = self._stream_wrapper + self._stream_wrapper = None + async with AsyncExitStack() as cleanup: + if stream_wrapper is not None: + + async def finalize_stream_wrapper() -> None: + if suppressed: + await stream_wrapper.__aexit__(None, None, None) + else: + await stream_wrapper.__aexit__( + exc_type, exc_val, exc_tb + ) + + cleanup.push_async_callback(finalize_stream_wrapper) + suppressed = await self._manager.__aexit__( + exc_type, exc_val, exc_tb + ) + return suppressed + + def __getattr__(self, name: str) -> object: + return getattr(self._manager, name) diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_stream_wrapper_finalize_idempotent.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_stream_wrapper_finalize_idempotent.yaml new file mode 100644 index 0000000000..742ed18b4f --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_stream_wrapper_finalize_idempotent.yaml @@ -0,0 +1,138 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '131' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01SzbrAKmQ4WKrcUssAJsSf3","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d65684f6e3fe0c6-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:21 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:20Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:20Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:20Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:20Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1141' + request-id: + - req_011CYfQNsrQ5w1kPtYjmXzeP + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '1051' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_aggregates_cache_tokens.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_aggregates_cache_tokens.yaml new file mode 100644 index 0000000000..db527c89c3 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_aggregates_cache_tokens.yaml @@ -0,0 +1,140 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '117' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |- + { + "model": "claude-sonnet-4-20250514", + "id": "msg_01QnfZn8pVXgBGu2wtj3EZKJ", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Hello!" + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 13, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 5, + "service_tier": "standard", + "inference_geo": "not_available" + } + } + headers: + CF-RAY: + - 9d656857bc9cc794-EWR + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 03 Mar 2026 03:03:22 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:22Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:22Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:21Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:22Z' + cf-cache-status: + - DYNAMIC + content-length: + - '441' + request-id: + - req_011CYfQNyftvAdchCgU7PwhE + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '911' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_api_error.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_api_error.yaml index 3940582198..875ba824e6 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_api_error.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_api_error.yaml @@ -1,343 +1,4 @@ interactions: -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Hello" - } - ], - "model": "invalid-model-name" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '110' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python - x-api-key: - - test_anthropic_api_key - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "not_found_error", - "message": "model: invalid-model-name" - } - } - headers: - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Mon, 15 Dec 2024 10:00:04 GMT - Server: - - cloudflare - content-length: - - '105' - status: - code: 404 - message: Not Found -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Hello" - } - ], - "model": "invalid-model-name" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '94' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "authentication_error", - "message": "invalid x-api-key" - }, - "request_id": "req_011CX88XGWSm82bN96ZkDWcr" - } - headers: - CF-RAY: - - 9be0e03eff016e28-EWR - Connection: - - keep-alive - Content-Length: - - '130' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:22:32 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88XGWSm82bN96ZkDWcr - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '13' - x-should-retry: - - 'false' - status: - code: 401 - message: Unauthorized -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Hello" - } - ], - "model": "invalid-model-name" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '94' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "invalid_request_error", - "message": "Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits." - }, - "request_id": "req_011CX88ZmBumGkvj7aK6Gqzx" - } - headers: - CF-RAY: - - 9be0e1127fd1b1bc-EWR - Connection: - - keep-alive - Content-Length: - - '234' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:23:06 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88ZmBumGkvj7aK6Gqzx - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '59' - x-should-retry: - - 'false' - status: - code: 400 - message: Bad Request -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Hello" - } - ], - "model": "invalid-model-name" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '94' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "not_found_error", - "message": "model: invalid-model-name" - }, - "request_id": "req_011CX89f9XqPgyRJC1ASfAab" - } - headers: - CF-RAY: - - 9be0f610add5b89f-EWR - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:37:25 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - content-length: - - '133' - request-id: - - req_011CX89f9XqPgyRJC1ASfAab - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '27' - x-should-retry: - - 'false' - status: - code: 404 - message: Not Found - request: body: |- { @@ -400,35 +61,37 @@ interactions: "type": "not_found_error", "message": "model: invalid-model-name" }, - "request_id": "req_011CX89kiWcGNsjEPrPGA42x" + "request_id": "req_011CYfQMhgSid28ainNjq126" } headers: CF-RAY: - - 9be0f7e8dbf70ab9-EWR + - 9d6567ebcfafc4fb-EWR Connection: - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' Content-Type: - application/json Date: - - Wed, 14 Jan 2026 23:38:41 GMT + - Tue, 03 Mar 2026 03:03:04 GMT Server: - cloudflare Transfer-Encoding: - chunked X-Robots-Tag: - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 cf-cache-status: - DYNAMIC content-length: - '133' request-id: - - req_011CX89kiWcGNsjEPrPGA42x + - req_011CYfQMhgSid28ainNjq126 strict-transport-security: - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding x-envoy-upstream-service-time: - - '78' + - '28' x-should-retry: - 'false' status: diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_basic.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_basic.yaml index 205b1c2e49..f7c1cab070 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_basic.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_basic.yaml @@ -1,720 +1,4 @@ interactions: -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hello in one word." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '128' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python - x-api-key: - - test_anthropic_api_key - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "id": "msg_01XFDUDYJgAACzvnptvVoYEL", - "type": "message", - "role": "assistant", - "model": "claude-3-5-sonnet-20241022", - "content": [ - { - "type": "text", - "text": "Hello!" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 14, - "output_tokens": 4 - } - } - headers: - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Mon, 15 Dec 2024 10:00:00 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - content-length: - - '350' - status: - code: 200 - message: OK -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hello in one word." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '119' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "authentication_error", - "message": "invalid x-api-key" - }, - "request_id": "req_011CX88X7DM3SrsuuERgZeYJ" - } - headers: - CF-RAY: - - 9be0e0315fa2a02c-EWR - Connection: - - keep-alive - Content-Length: - - '130' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:22:30 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88X7DM3SrsuuERgZeYJ - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '13' - x-should-retry: - - 'false' - status: - code: 401 - message: Unauthorized -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hello in one word." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '119' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "invalid_request_error", - "message": "Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits." - }, - "request_id": "req_011CX88Zakc7NS5rDAkMMK45" - } - headers: - CF-RAY: - - 9be0e1032e73ace5-EWR - Connection: - - keep-alive - Content-Length: - - '234' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:23:03 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88Zakc7NS5rDAkMMK45 - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '30' - x-should-retry: - - 'false' - status: - code: 400 - message: Bad Request -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hello in one word." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '119' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "invalid_request_error", - "message": "Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits." - }, - "request_id": "req_011CX88qYh2YvHnk7Hp8vCR7" - } - headers: - CF-RAY: - - 9be0e64cc92eb4c6-EWR - Connection: - - keep-alive - Content-Length: - - '234' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:26:40 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88qYh2YvHnk7Hp8vCR7 - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '20' - x-should-retry: - - 'false' - status: - code: 400 - message: Bad Request -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hello in one word." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '119' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "not_found_error", - "message": "model: claude-3-5-sonnet-20241022" - }, - "request_id": "req_011CX89Wba1H8DoNZMAq5M9M" - } - headers: - CF-RAY: - - 9be0f33baa01cdf0-EWR - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:35:29 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - content-length: - - '141' - request-id: - - req_011CX89Wba1H8DoNZMAq5M9M - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '28' - x-should-retry: - - 'false' - status: - code: 404 - message: Not Found -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hello in one word." - } - ], - "model": "claude-sonnet-4-20250514" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '117' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "model": "claude-sonnet-4-20250514", - "id": "msg_01ChhLEFb4TSQWHpQzFqEQsj", - "type": "message", - "role": "assistant", - "content": [ - { - "type": "text", - "text": "Hello!" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 13, - "cache_creation_input_tokens": 0, - "cache_read_input_tokens": 0, - "cache_creation": { - "ephemeral_5m_input_tokens": 0, - "ephemeral_1h_input_tokens": 0 - }, - "output_tokens": 5, - "service_tier": "standard" - } - } - headers: - CF-RAY: - - 9be0f55b2b560866-EWR - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:36:58 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - anthropic-ratelimit-input-tokens-limit: - - '30000' - anthropic-ratelimit-input-tokens-remaining: - - '30000' - anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:36:58Z' - anthropic-ratelimit-output-tokens-limit: - - '8000' - anthropic-ratelimit-output-tokens-remaining: - - '8000' - anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:36:58Z' - anthropic-ratelimit-requests-limit: - - '50' - anthropic-ratelimit-requests-remaining: - - '49' - anthropic-ratelimit-requests-reset: - - '2026-01-14T23:36:58Z' - anthropic-ratelimit-tokens-limit: - - '38000' - anthropic-ratelimit-tokens-remaining: - - '38000' - anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:36:58Z' - cf-cache-status: - - DYNAMIC - content-length: - - '409' - request-id: - - req_011CX89d1Mu8qapBc5y9KdXf - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '1596' - status: - code: 200 - message: OK -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hello in one word." - } - ], - "model": "claude-sonnet-4-20250514" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '117' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "model": "claude-sonnet-4-20250514", - "id": "msg_01W7B22k9o9QrCqiEWmU1v9G", - "type": "message", - "role": "assistant", - "content": [ - { - "type": "text", - "text": "Hello!" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 13, - "cache_creation_input_tokens": 0, - "cache_read_input_tokens": 0, - "cache_creation": { - "ephemeral_5m_input_tokens": 0, - "ephemeral_1h_input_tokens": 0 - }, - "output_tokens": 5, - "service_tier": "standard" - } - } - headers: - CF-RAY: - - 9be0f5d0cffcdcde-EWR - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:37:17 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - anthropic-ratelimit-input-tokens-limit: - - '30000' - anthropic-ratelimit-input-tokens-remaining: - - '30000' - anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:37:16Z' - anthropic-ratelimit-output-tokens-limit: - - '8000' - anthropic-ratelimit-output-tokens-remaining: - - '8000' - anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:37:17Z' - anthropic-ratelimit-requests-limit: - - '50' - anthropic-ratelimit-requests-remaining: - - '49' - anthropic-ratelimit-requests-reset: - - '2026-01-14T23:37:16Z' - anthropic-ratelimit-tokens-limit: - - '38000' - anthropic-ratelimit-tokens-remaining: - - '38000' - anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:37:16Z' - cf-cache-status: - - DYNAMIC - content-length: - - '409' - request-id: - - req_011CX89ePqXjam4ByzovDWC6 - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '1516' - status: - code: 200 - message: OK - request: body: |- { @@ -773,7 +57,7 @@ interactions: string: |- { "model": "claude-sonnet-4-20250514", - "id": "msg_01MpMMdwiz43MhCffJBjWRZP", + "id": "msg_0176GK1qFwwpVM59jDYiKPjN", "type": "message", "role": "assistant", "content": [ @@ -793,60 +77,63 @@ interactions: "ephemeral_1h_input_tokens": 0 }, "output_tokens": 5, - "service_tier": "standard" + "service_tier": "standard", + "inference_geo": "not_available" } } headers: CF-RAY: - - 9be0f7a98b01b734-EWR + - 9d6567b84d8509ae-EWR Connection: - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' Content-Type: - application/json Date: - - Wed, 14 Jan 2026 23:38:33 GMT + - Tue, 03 Mar 2026 03:02:57 GMT Server: - cloudflare Transfer-Encoding: - chunked X-Robots-Tag: - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 anthropic-ratelimit-input-tokens-limit: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-remaining: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:38:33Z' + - '2026-03-03T03:02:57Z' anthropic-ratelimit-output-tokens-limit: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-remaining: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:38:33Z' + - '2026-03-03T03:02:57Z' anthropic-ratelimit-requests-limit: - - '50' + - '1000' anthropic-ratelimit-requests-remaining: - - '49' + - '999' anthropic-ratelimit-requests-reset: - - '2026-01-14T23:38:32Z' + - '2026-03-03T03:02:56Z' anthropic-ratelimit-tokens-limit: - - '38000' + - '540000' anthropic-ratelimit-tokens-remaining: - - '38000' + - '540000' anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:38:33Z' + - '2026-03-03T03:02:57Z' cf-cache-status: - DYNAMIC content-length: - - '409' + - '441' request-id: - - req_011CX89jyLYwphKuArFEcRij + - req_011CYfQM6TP1iFTVWNrJQJcn strict-transport-security: - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding x-envoy-upstream-service-time: - - '1970' + - '1056' status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_content.yaml new file mode 100644 index 0000000000..3056b11822 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_content.yaml @@ -0,0 +1,140 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '117' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |- + { + "model": "claude-sonnet-4-20250514", + "id": "msg_01HZQzQu6SDuERFEAD5E2LLE", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Hello!" + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 13, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 5, + "service_tier": "standard", + "inference_geo": "not_available" + } + } + headers: + CF-RAY: + - 9d6567bfdaa9c882-EWR + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 03 Mar 2026 03:02:58 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:02:58Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:02:58Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:02:57Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:02:58Z' + cf-cache-status: + - DYNAMIC + content-length: + - '441' + request-id: + - req_011CYfQMBeAfBDq653smhx2t + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '977' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_thinking_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_thinking_content.yaml new file mode 100644 index 0000000000..846cc9dca2 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_thinking_content.yaml @@ -0,0 +1,149 @@ +interactions: +- request: + body: |- + { + "max_tokens": 16000, + "messages": [ + { + "role": "user", + "content": "What is 17*19? Think first." + } + ], + "model": "claude-sonnet-4-20250514", + "thinking": { + "type": "enabled", + "budget_tokens": 10000 + } + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '176' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |- + { + "model": "claude-sonnet-4-20250514", + "id": "msg_01NgMPJmYbigmhddjGchjPqH", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "thinking", + "thinking": "I need to calculate 17 \u00d7 19.\n\nLet me think about this step by step. I can use the standard multiplication method or look for patterns.\n\nOne approach is to use the fact that these are close to 20:\n17 \u00d7 19 = (20 - 3) \u00d7 (20 - 1)\n= 20\u00b2 - 20(1) - 20(3) + 3(1)\n= 400 - 20 - 60 + 3\n= 400 - 80 + 3\n= 323\n\nLet me double-check this with standard multiplication:\n 17\n\u00d7 19\n----\n 153 (17 \u00d7 9)\n170 (17 \u00d7 10)\n----\n323\n\nLet me verify the first line: 17 \u00d7 9\n17 \u00d7 9 = 17 \u00d7 (10 - 1) = 170 - 17 = 153 \u2713\n\nAnd 17 \u00d7 10 = 170 \u2713\n\nSo 153 + 170 = 323 \u2713\n\nBoth methods give me 323.", + "signature": "EuYFCkYICxgCKkDxKjCDNXmiJ72KrQXA/ugiHjqc/OWa4dnEXu4j+v4zmcZ4+W+F7rJXdg+tktg1xtGZ1q8YV8ks3D5z7qKU4wBcEgxLejbEBZAporXYjDAaDLjUh8AZqzczN1RQayIwf7KXei2iS5bdPbz+Uerq2AhvwjfWVyXrz4knPkQZPb1e6+BGrUfp1fEkYPxzUn4fKs0E4Y0nqE6xMzp9o1qOo3ZoBLGje2C8TMR82vE/9Ms2b4N088akwcPd6t7UkL3X5pb0FgXbdrNdhWOq66wnNZvLsVXsnRIoOdX2xculb5PJvQRgu+Pz8VXWwEsGZlmlhh/Zio+w1n6SELCpwahZPLdVGgKMssZ17B9Dc8SKldP6V8Yk4LtJLZDAnT0rbGVb92OUjEuYstDuBYwTur6cuQMUVZeNHQyLXybXDxGKxJ7yKndWOormD+LZOLveaeM+ttgpR/LH/lBaeLfaX0ft4ATSCGeZf2xysRCxUa6qco4Fs4BGU8e9RKC7ztT6SS/R2X9P15cD3fn9EfdH57CzvL3gBfrpSqcCnhV1DR9nlOZ5AJ8bfyOS4bPZN8bM6R4uPZmjUiItX7kmEZeUHt/GJQsdJAtb1/f/AGRXYyE/8/VL2yxB+q3PSsbVqSABqSAcLd9q5gl4nF9lkRhNcty8XznUjIanwCpjAJbLceE+Ml5nlYLDjaQhKEzwrmZnSZmCCjhi2BfN8hnBPh+SGuDAjP+J7aIOcY5LuJg836vdEwIRjSY73plwdf/h/RpEspc/uCspxMRQbCHigXwRpotTlAOHBJPYuoX5b5Ka2MpyUDAIBneDE44EQnMUJgv6LW6NrIOs2lp9FHMGEcfUZfnXK+zMQcq2wyHWrufNl/jPkj6BBQpflPZDNHudoPBm6WsIYgDTcrzam+zqLEUoH81zG9npzTzrRiwK2HPNfkoJk62eL67gJLE7jRK3v8r9IPVkPMYwiP0Yd6VRt6tZzjk1NhgB" + }, + { + "type": "text", + "text": "I need to calculate 17 \u00d7 19.\n\nLet me work through this step by step:\n\n17 \u00d7 19\n\nI can use the standard multiplication method:\n```\n 17\n\u00d7 19\n-----\n 153 (17 \u00d7 9)\n 170 (17 \u00d7 10, shifted)\n-----\n 323\n```\n\nLet me verify:\n- 17 \u00d7 9 = 153 \u2713\n- 17 \u00d7 10 = 170 \u2713 \n- 153 + 170 = 323 \u2713\n\nTherefore, 17 \u00d7 19 = 323." + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 46, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 417, + "service_tier": "standard", + "inference_geo": "not_available" + } + } + headers: + CF-RAY: + - 9d65681d9c250fa4-EWR + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 03 Mar 2026 03:03:20 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:14Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:20Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:12Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:14Z' + cf-cache-status: + - DYNAMIC + content-length: + - '2421' + request-id: + - req_011CYfQNHwDY25u9A4Ss1BaU + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '7718' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_tool_use_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_tool_use_content.yaml new file mode 100644 index 0000000000..85d6bc4dd1 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_captures_tool_use_content.yaml @@ -0,0 +1,168 @@ +interactions: +- request: + body: |- + { + "max_tokens": 256, + "messages": [ + { + "role": "user", + "content": "What is the weather in SF?" + } + ], + "model": "claude-sonnet-4-20250514", + "tool_choice": { + "type": "tool", + "name": "get_weather" + }, + "tools": [ + { + "name": "get_weather", + "description": "Get weather by city", + "input_schema": { + "type": "object", + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ] + } + } + ] + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '334' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |- + { + "model": "claude-sonnet-4-20250514", + "id": "msg_01VkeD2PaERGgjekk84zPSwr", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "toolu_01UAcA2APHENDob6qJ8kmoaC", + "name": "get_weather", + "input": { + "city": "San Francisco" + }, + "caller": { + "type": "direct" + } + } + ], + "stop_reason": "tool_use", + "stop_sequence": null, + "usage": { + "input_tokens": 386, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 34, + "service_tier": "standard", + "inference_geo": "not_available" + } + } + headers: + CF-RAY: + - 9d656813eddfae20-EWR + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 03 Mar 2026 03:03:11 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:11Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:11Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:10Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:11Z' + cf-cache-status: + - DYNAMIC + content-length: + - '550' + request-id: + - req_011CYfQNBARQegAkczG6kweQ + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '1256' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_event_only_no_content_in_span.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_event_only_no_content_in_span.yaml new file mode 100644 index 0000000000..eb8b720299 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_event_only_no_content_in_span.yaml @@ -0,0 +1,140 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '117' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |- + { + "model": "claude-sonnet-4-20250514", + "id": "msg_01FaPr9xcPcSjcy2XAU85Pyj", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Hello!" + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 13, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 0 + }, + "output_tokens": 5, + "service_tier": "standard", + "inference_geo": "not_available" + } + } + headers: + CF-RAY: + - 9d65687b3b23134a-EWR + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 03 Mar 2026 03:03:28 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:28Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:28Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:27Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:28Z' + cf-cache-status: + - DYNAMIC + content-length: + - '441' + request-id: + - req_011CYfQPPniijYhRkF7mjRn4 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '905' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_instrumentation_error_swallowed.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_instrumentation_error_swallowed.yaml new file mode 100644 index 0000000000..ef4a43e22a --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_instrumentation_error_swallowed.yaml @@ -0,0 +1,138 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '131' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01NpSWjDnYM3G9wFsxNwNaBh","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0} + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} } + + event: message_stop + data: {"type":"message_stop"} + + headers: + CF-RAY: + - 9d6568736addb785-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:27 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:26Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:26Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:26Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:26Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1094' + request-id: + - req_011CYfQPJSkizN91DEN4ZUpZ + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '1001' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_stop_reason.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_stop_reason.yaml index 17e0fb8715..a314103663 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_stop_reason.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_stop_reason.yaml @@ -1,395 +1,4 @@ interactions: -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hi." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '114' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python - x-api-key: - - test_anthropic_api_key - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "id": "msg_04AGDUDYJgAACzvnptvVoYEL", - "type": "message", - "role": "assistant", - "model": "claude-3-5-sonnet-20241022", - "content": [ - { - "type": "text", - "text": "Hi!" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 10, - "output_tokens": 3 - } - } - headers: - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Mon, 15 Dec 2024 10:00:03 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - content-length: - - '340' - status: - code: 200 - message: OK -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hi." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '104' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "authentication_error", - "message": "invalid x-api-key" - }, - "request_id": "req_011CX88XA9TbQ5AUJfKb95H1" - } - headers: - CF-RAY: - - 9be0e035a86cc60f-EWR - Connection: - - keep-alive - Content-Length: - - '130' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:22:30 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88XA9TbQ5AUJfKb95H1 - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '14' - x-should-retry: - - 'false' - status: - code: 401 - message: Unauthorized -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hi." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '104' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "invalid_request_error", - "message": "Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits." - }, - "request_id": "req_011CX88Ze7X2Hzt93cQSJVwx" - } - headers: - CF-RAY: - - 9be0e1081e29cb6b-EWR - Connection: - - keep-alive - Content-Length: - - '234' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:23:04 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88Ze7X2Hzt93cQSJVwx - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '32' - x-should-retry: - - 'false' - status: - code: 400 - message: Bad Request -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Say hi." - } - ], - "model": "claude-sonnet-4-20250514" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '102' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "model": "claude-sonnet-4-20250514", - "id": "msg_0179PsyreizP7wUuiZek9cY1", - "type": "message", - "role": "assistant", - "content": [ - { - "type": "text", - "text": "Hi! How are you doing today?" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 10, - "cache_creation_input_tokens": 0, - "cache_read_input_tokens": 0, - "cache_creation": { - "ephemeral_5m_input_tokens": 0, - "ephemeral_1h_input_tokens": 0 - }, - "output_tokens": 11, - "service_tier": "standard" - } - } - headers: - CF-RAY: - - 9be0f5f8dbb9c484-EWR - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:37:24 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - anthropic-ratelimit-input-tokens-limit: - - '30000' - anthropic-ratelimit-input-tokens-remaining: - - '30000' - anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:37:23Z' - anthropic-ratelimit-output-tokens-limit: - - '8000' - anthropic-ratelimit-output-tokens-remaining: - - '8000' - anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:37:24Z' - anthropic-ratelimit-requests-limit: - - '50' - anthropic-ratelimit-requests-remaining: - - '49' - anthropic-ratelimit-requests-reset: - - '2026-01-14T23:37:23Z' - anthropic-ratelimit-tokens-limit: - - '38000' - anthropic-ratelimit-tokens-remaining: - - '38000' - anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:37:23Z' - cf-cache-status: - - DYNAMIC - content-length: - - '432' - request-id: - - req_011CX89esEoi7zVpEaWsLZc8 - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '2140' - status: - code: 200 - message: OK - request: body: |- { @@ -448,7 +57,7 @@ interactions: string: |- { "model": "claude-sonnet-4-20250514", - "id": "msg_015tg8KmiFK3cef86zqT6mbU", + "id": "msg_012d8JRV176rPaczDFDxoDbT", "type": "message", "role": "assistant", "content": [ @@ -468,60 +77,63 @@ interactions: "ephemeral_1h_input_tokens": 0 }, "output_tokens": 11, - "service_tier": "standard" + "service_tier": "standard", + "inference_geo": "not_available" } } headers: CF-RAY: - - 9be0f7d4490ae55d-EWR + - 9d6567d9c9c5643e-EWR Connection: - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' Content-Type: - application/json Date: - - Wed, 14 Jan 2026 23:38:39 GMT + - Tue, 03 Mar 2026 03:03:02 GMT Server: - cloudflare Transfer-Encoding: - chunked X-Robots-Tag: - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 anthropic-ratelimit-input-tokens-limit: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-remaining: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:38:39Z' + - '2026-03-03T03:03:02Z' anthropic-ratelimit-output-tokens-limit: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-remaining: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:38:39Z' + - '2026-03-03T03:03:02Z' anthropic-ratelimit-requests-limit: - - '50' + - '1000' anthropic-ratelimit-requests-remaining: - - '49' + - '999' anthropic-ratelimit-requests-reset: - - '2026-01-14T23:38:39Z' + - '2026-03-03T03:03:01Z' anthropic-ratelimit-tokens-limit: - - '38000' + - '540000' anthropic-ratelimit-tokens-remaining: - - '38000' + - '540000' anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:38:39Z' + - '2026-03-03T03:03:02Z' cf-cache-status: - DYNAMIC content-length: - - '432' + - '464' request-id: - - req_011CX89kUUFsmSE8auY18PrD + - req_011CYfQMVM2pA3EVbN8Ja2sr strict-transport-security: - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding x-envoy-upstream-service-time: - - '1745' + - '1244' status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_stream_propagation_error.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_stream_propagation_error.yaml new file mode 100644 index 0000000000..f936fe4eee --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_stream_propagation_error.yaml @@ -0,0 +1,138 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '131' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_014i5i2v6887MWQQjqctDRHo","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d656865ccea2ef5-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:24 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:23Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:23Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:23Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:23Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1132' + request-id: + - req_011CYfQP99QU9m2UtNA5BFZP + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '721' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming.yaml new file mode 100644 index 0000000000..7c2f521846 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming.yaml @@ -0,0 +1,138 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '131' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01VY8H3oPFs4WWVnJXFDxNhU","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello."} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d6567ecfaf86dc6-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:05 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:04Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:04Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:04Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:04Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1126' + request-id: + - req_011CYfQMiWJUo29ePpD2ATbD + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '1037' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_aggregates_cache_tokens.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_aggregates_cache_tokens.yaml new file mode 100644 index 0000000000..1e2d98dd0a --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_aggregates_cache_tokens.yaml @@ -0,0 +1,138 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '131' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01BpXsyrS2qCTuzQnTbLsB9w","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d65685e5bc3cc98-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:23 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:22Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:22Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:22Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:22Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1134' + request-id: + - req_011CYfQP44pFSHNJSnZKmaAe + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '957' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_captures_content.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_captures_content.yaml new file mode 100644 index 0000000000..f6599c15e4 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_captures_content.yaml @@ -0,0 +1,138 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '131' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01VkmtVzTqckWr4Ym4746Lid","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d6567f53bf6c8b9-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:06 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:05Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:05Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:05Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:05Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1116' + request-id: + - req_011CYfQMpH4nmsRYY8zj6kCw + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '1028' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_delegates_response_attribute.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_delegates_response_attribute.yaml new file mode 100644 index 0000000000..372d88d8ff --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_delegates_response_attribute.yaml @@ -0,0 +1,147 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hi." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '116' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01RJVipYo4aevccmbTqgpe2d","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hi"} } + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"! How"} } + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" are"} } + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" you doing today?"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":11} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d656804cf5d0f84-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:09 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:08Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:08Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:08Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:08Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1540' + request-id: + - req_011CYfQMznqKRDJdKG8StgR4 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '741' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_iteration.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_iteration.yaml new file mode 100644 index 0000000000..1f53042256 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_iteration.yaml @@ -0,0 +1,147 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hi." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '116' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01AqjPamWtxvjFRhdM532SJo","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hi"} } + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"! How"} } + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" are"} } + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" you doing today?"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":11} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d6567fd7d44c269-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:07 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:07Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:07Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:07Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:07Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1519' + request-id: + - req_011CYfQMuohKD5gG9juMpkR9 + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '784' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_user_exception.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_user_exception.yaml new file mode 100644 index 0000000000..38dd8a1e58 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_streaming_user_exception.yaml @@ -0,0 +1,138 @@ +interactions: +- request: + body: |- + { + "max_tokens": 100, + "messages": [ + { + "role": "user", + "content": "Say hello in one word." + } + ], + "model": "claude-sonnet-4-20250514", + "stream": true + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '131' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.75.0 + x-api-key: + - test_anthropic_api_key + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.75.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.9.6 + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |+ + event: message_start + data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01KTDb2NA7N8ZMdCVJi55gsc","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"}} } + + event: content_block_start + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + event: ping + data: {"type": "ping"} + + event: content_block_delta + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello!"} } + + event: content_block_stop + data: {"type":"content_block_stop","index":0 } + + event: message_delta + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":13,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5} } + + event: message_stop + data: {"type":"message_stop" } + + headers: + CF-RAY: + - 9d65686baf371a38-EWR + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 03 Mar 2026 03:03:25 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-ratelimit-input-tokens-limit: + - '450000' + anthropic-ratelimit-input-tokens-remaining: + - '450000' + anthropic-ratelimit-input-tokens-reset: + - '2026-03-03T03:03:24Z' + anthropic-ratelimit-output-tokens-limit: + - '90000' + anthropic-ratelimit-output-tokens-remaining: + - '90000' + anthropic-ratelimit-output-tokens-reset: + - '2026-03-03T03:03:24Z' + anthropic-ratelimit-requests-limit: + - '1000' + anthropic-ratelimit-requests-remaining: + - '999' + anthropic-ratelimit-requests-reset: + - '2026-03-03T03:03:24Z' + anthropic-ratelimit-tokens-limit: + - '540000' + anthropic-ratelimit-tokens-remaining: + - '540000' + anthropic-ratelimit-tokens-reset: + - '2026-03-03T03:03:24Z' + cf-cache-status: + - DYNAMIC + content-length: + - '1125' + request-id: + - req_011CYfQPDB12HAd3CZ3B6udD + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '962' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_token_usage.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_token_usage.yaml index e6f90a0951..f49d27e0ec 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_token_usage.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_token_usage.yaml @@ -1,395 +1,4 @@ interactions: -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Count to 5." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '118' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python - x-api-key: - - test_anthropic_api_key - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "id": "msg_03ZGDUDYJgAACzvnptvVoYEL", - "type": "message", - "role": "assistant", - "model": "claude-3-5-sonnet-20241022", - "content": [ - { - "type": "text", - "text": "1, 2, 3, 4, 5" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 12, - "output_tokens": 14 - } - } - headers: - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Mon, 15 Dec 2024 10:00:02 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - content-length: - - '355' - status: - code: 200 - message: OK -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Count to 5." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '108' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "authentication_error", - "message": "invalid x-api-key" - }, - "request_id": "req_011CX88X9HN93DBTEXsjo9DZ" - } - headers: - CF-RAY: - - 9be0e0346f01a0f4-EWR - Connection: - - keep-alive - Content-Length: - - '130' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:22:30 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88X9HN93DBTEXsjo9DZ - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '11' - x-should-retry: - - 'false' - status: - code: 401 - message: Unauthorized -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Count to 5." - } - ], - "model": "claude-3-5-sonnet-20241022" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '108' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "invalid_request_error", - "message": "Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits." - }, - "request_id": "req_011CX88Zd4WpWvH6NbSKLd4T" - } - headers: - CF-RAY: - - 9be0e1068d28f9a9-EWR - Connection: - - keep-alive - Content-Length: - - '234' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:23:04 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88Zd4WpWvH6NbSKLd4T - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '23' - x-should-retry: - - 'false' - status: - code: 400 - message: Bad Request -- request: - body: |- - { - "max_tokens": 100, - "messages": [ - { - "role": "user", - "content": "Count to 5." - } - ], - "model": "claude-sonnet-4-20250514" - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '106' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "model": "claude-sonnet-4-20250514", - "id": "msg_01ASpi9ptEqzyzyCGk3aB1tr", - "type": "message", - "role": "assistant", - "content": [ - { - "type": "text", - "text": "1\n2\n3\n4\n5" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 12, - "cache_creation_input_tokens": 0, - "cache_read_input_tokens": 0, - "cache_creation": { - "ephemeral_5m_input_tokens": 0, - "ephemeral_1h_input_tokens": 0 - }, - "output_tokens": 13, - "service_tier": "standard" - } - } - headers: - CF-RAY: - - 9be0f5e96d19cc98-EWR - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:37:21 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - anthropic-ratelimit-input-tokens-limit: - - '30000' - anthropic-ratelimit-input-tokens-remaining: - - '30000' - anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:37:21Z' - anthropic-ratelimit-output-tokens-limit: - - '8000' - anthropic-ratelimit-output-tokens-remaining: - - '8000' - anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:37:21Z' - anthropic-ratelimit-requests-limit: - - '50' - anthropic-ratelimit-requests-remaining: - - '49' - anthropic-ratelimit-requests-reset: - - '2026-01-14T23:37:20Z' - anthropic-ratelimit-tokens-limit: - - '38000' - anthropic-ratelimit-tokens-remaining: - - '38000' - anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:37:21Z' - cf-cache-status: - - DYNAMIC - content-length: - - '417' - request-id: - - req_011CX89egfZS3S8npjMnb4jb - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '2246' - status: - code: 200 - message: OK - request: body: |- { @@ -448,7 +57,7 @@ interactions: string: |- { "model": "claude-sonnet-4-20250514", - "id": "msg_01JFQS8RhcxvidJNQvmANwSp", + "id": "msg_015sFGNKbMjgUZs7UU75pU6d", "type": "message", "role": "assistant", "content": [ @@ -468,60 +77,63 @@ interactions: "ephemeral_1h_input_tokens": 0 }, "output_tokens": 17, - "service_tier": "standard" + "service_tier": "standard", + "inference_geo": "not_available" } } headers: CF-RAY: - - 9be0f7c51d21c62c-EWR + - 9d6567cfabc719c7-EWR Connection: - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' Content-Type: - application/json Date: - - Wed, 14 Jan 2026 23:38:37 GMT + - Tue, 03 Mar 2026 03:03:01 GMT Server: - cloudflare Transfer-Encoding: - chunked X-Robots-Tag: - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 anthropic-ratelimit-input-tokens-limit: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-remaining: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:38:36Z' + - '2026-03-03T03:03:00Z' anthropic-ratelimit-output-tokens-limit: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-remaining: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:38:38Z' + - '2026-03-03T03:03:01Z' anthropic-ratelimit-requests-limit: - - '50' + - '1000' anthropic-ratelimit-requests-remaining: - - '49' + - '999' anthropic-ratelimit-requests-reset: - - '2026-01-14T23:38:36Z' + - '2026-03-03T03:02:59Z' anthropic-ratelimit-tokens-limit: - - '38000' + - '540000' anthropic-ratelimit-tokens-remaining: - - '38000' + - '540000' anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:38:36Z' + - '2026-03-03T03:03:00Z' cf-cache-status: - DYNAMIC content-length: - - '417' + - '449' request-id: - - req_011CX89kJ5gA2k39mva9DeiB + - req_011CYfQMNSoikmPWgmgcCCJb strict-transport-security: - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding x-envoy-upstream-service-time: - - '2244' + - '1464' status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_with_all_params.yaml b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_with_all_params.yaml index b63b5b56da..6fad0a4c90 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_with_all_params.yaml +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/cassettes/test_sync_messages_create_with_all_params.yaml @@ -1,419 +1,4 @@ interactions: -- request: - body: |- - { - "max_tokens": 50, - "messages": [ - { - "role": "user", - "content": "Say hello." - } - ], - "model": "claude-3-5-sonnet-20241022", - "stop_sequences": [ - "STOP" - ], - "temperature": 0.7, - "top_k": 40, - "top_p": 0.9 - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '200' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python - x-api-key: - - test_anthropic_api_key - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "id": "msg_02YGDUDYJgAACzvnptvVoYEL", - "type": "message", - "role": "assistant", - "model": "claude-3-5-sonnet-20241022", - "content": [ - { - "type": "text", - "text": "Hello! How can I help you today?" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 10, - "output_tokens": 10 - } - } - headers: - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Mon, 15 Dec 2024 10:00:01 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - content-length: - - '380' - status: - code: 200 - message: OK -- request: - body: |- - { - "max_tokens": 50, - "messages": [ - { - "role": "user", - "content": "Say hello." - } - ], - "model": "claude-3-5-sonnet-20241022", - "stop_sequences": [ - "STOP" - ], - "temperature": 0.7, - "top_k": 40, - "top_p": 0.9 - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '173' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "authentication_error", - "message": "invalid x-api-key" - }, - "request_id": "req_011CX88X8R2SNXCvHpf8jdxa" - } - headers: - CF-RAY: - - 9be0e0331fe3effa-EWR - Connection: - - keep-alive - Content-Length: - - '130' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:22:30 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88X8R2SNXCvHpf8jdxa - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '14' - x-should-retry: - - 'false' - status: - code: 401 - message: Unauthorized -- request: - body: |- - { - "max_tokens": 50, - "messages": [ - { - "role": "user", - "content": "Say hello." - } - ], - "model": "claude-3-5-sonnet-20241022", - "stop_sequences": [ - "STOP" - ], - "temperature": 0.7, - "top_k": 40, - "top_p": 0.9 - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '173' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "type": "error", - "error": { - "type": "invalid_request_error", - "message": "Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits." - }, - "request_id": "req_011CX88Zc1kwbPkJkhghetKz" - } - headers: - CF-RAY: - - 9be0e10508204ba5-EWR - Connection: - - keep-alive - Content-Length: - - '234' - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:23:03 GMT - Server: - - cloudflare - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX88Zc1kwbPkJkhghetKz - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '31' - x-should-retry: - - 'false' - status: - code: 400 - message: Bad Request -- request: - body: |- - { - "max_tokens": 50, - "messages": [ - { - "role": "user", - "content": "Say hello." - } - ], - "model": "claude-sonnet-4-20250514", - "stop_sequences": [ - "STOP" - ], - "temperature": 0.7, - "top_k": 40, - "top_p": 0.9 - } - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - anthropic-version: - - '2023-06-01' - connection: - - keep-alive - content-length: - - '171' - content-type: - - application/json - host: - - api.anthropic.com - user-agent: - - Anthropic/Python 0.75.0 - x-api-key: - - test_anthropic_api_key - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 0.75.0 - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.9.6 - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: |- - { - "model": "claude-sonnet-4-20250514", - "id": "msg_01Nf6xgm48TeELiSicTA83cX", - "type": "message", - "role": "assistant", - "content": [ - { - "type": "text", - "text": "Hello! How are you doing today?" - } - ], - "stop_reason": "end_turn", - "stop_sequence": null, - "usage": { - "input_tokens": 10, - "cache_creation_input_tokens": 0, - "cache_read_input_tokens": 0, - "cache_creation": { - "ephemeral_5m_input_tokens": 0, - "ephemeral_1h_input_tokens": 0 - }, - "output_tokens": 11, - "service_tier": "standard" - } - } - headers: - CF-RAY: - - 9be0f5db9b8110f3-EWR - Connection: - - keep-alive - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 23:37:19 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 - anthropic-ratelimit-input-tokens-limit: - - '30000' - anthropic-ratelimit-input-tokens-remaining: - - '30000' - anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:37:18Z' - anthropic-ratelimit-output-tokens-limit: - - '8000' - anthropic-ratelimit-output-tokens-remaining: - - '8000' - anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:37:19Z' - anthropic-ratelimit-requests-limit: - - '50' - anthropic-ratelimit-requests-remaining: - - '49' - anthropic-ratelimit-requests-reset: - - '2026-01-14T23:37:18Z' - anthropic-ratelimit-tokens-limit: - - '38000' - anthropic-ratelimit-tokens-remaining: - - '38000' - anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:37:18Z' - cf-cache-status: - - DYNAMIC - content-length: - - '435' - request-id: - - req_011CX89eXCYZvbfUZDzWQK3e - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '1953' - status: - code: 200 - message: OK - request: body: |- { @@ -478,13 +63,13 @@ interactions: string: |- { "model": "claude-sonnet-4-20250514", - "id": "msg_015XXNApQAi4ZgazLmEDHne6", + "id": "msg_015vKsHVeGGXxG7Q49sUsfwG", "type": "message", "role": "assistant", "content": [ { "type": "text", - "text": "Hello! How are you doing today?" + "text": "Hello! It's nice to meet you. How are you doing today?" } ], "stop_reason": "end_turn", @@ -497,61 +82,64 @@ interactions: "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, - "output_tokens": 11, - "service_tier": "standard" + "output_tokens": 18, + "service_tier": "standard", + "inference_geo": "not_available" } } headers: CF-RAY: - - 9be0f7b73af7c269-EWR + - 9d6567c6f9f51016-EWR Connection: - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' Content-Type: - application/json Date: - - Wed, 14 Jan 2026 23:38:35 GMT + - Tue, 03 Mar 2026 03:02:59 GMT Server: - cloudflare Transfer-Encoding: - chunked X-Robots-Tag: - none - anthropic-organization-id: - - 455ea6be-bd92-4199-83ec-0c6b39c5c169 anthropic-ratelimit-input-tokens-limit: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-remaining: - - '30000' + - '450000' anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T23:38:35Z' + - '2026-03-03T03:02:59Z' anthropic-ratelimit-output-tokens-limit: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-remaining: - - '8000' + - '90000' anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T23:38:35Z' + - '2026-03-03T03:02:59Z' anthropic-ratelimit-requests-limit: - - '50' + - '1000' anthropic-ratelimit-requests-remaining: - - '49' + - '999' anthropic-ratelimit-requests-reset: - - '2026-01-14T23:38:34Z' + - '2026-03-03T03:02:58Z' anthropic-ratelimit-tokens-limit: - - '38000' + - '540000' anthropic-ratelimit-tokens-remaining: - - '38000' + - '540000' anthropic-ratelimit-tokens-reset: - - '2026-01-14T23:38:35Z' + - '2026-03-03T03:02:59Z' cf-cache-status: - DYNAMIC content-length: - - '435' + - '490' request-id: - - req_011CX89k8aBRkvFFLPZSBSrX + - req_011CYfQMGWs6c2NPKvC1SwEp strict-transport-security: - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding x-envoy-upstream-service-time: - - '1955' + - '1225' status: code: 200 message: OK diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/conftest.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/conftest.py index 9f660d1fd4..c9573d8251 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/conftest.py +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/conftest.py @@ -22,6 +22,10 @@ import yaml from anthropic import Anthropic +from opentelemetry.instrumentation._semconv import ( + OTEL_SEMCONV_STABILITY_OPT_IN, + _OpenTelemetrySemanticConventionStability, +) from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk._logs.export import ( @@ -37,6 +41,7 @@ ) from opentelemetry.util.genai.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, + OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT, ) @@ -115,8 +120,12 @@ def vcr_config(): @pytest.fixture(scope="function") def instrument_no_content(tracer_provider, logger_provider, meter_provider): """Instrument Anthropic without content capture.""" + _OpenTelemetrySemanticConventionStability._initialized = False os.environ.update( - {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "False"} + { + OTEL_SEMCONV_STABILITY_OPT_IN: "stable", + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "NO_CONTENT", + } ) instrumentor = AnthropicInstrumentor() @@ -126,16 +135,26 @@ def instrument_no_content(tracer_provider, logger_provider, meter_provider): meter_provider=meter_provider, ) - yield instrumentor - os.environ.pop(OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, None) - instrumentor.uninstrument() + try: + yield instrumentor + finally: + os.environ.pop( + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, None + ) + os.environ.pop(OTEL_SEMCONV_STABILITY_OPT_IN, None) + instrumentor.uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False @pytest.fixture(scope="function") def instrument_with_content(tracer_provider, logger_provider, meter_provider): """Instrument Anthropic with content capture enabled.""" + _OpenTelemetrySemanticConventionStability._initialized = False os.environ.update( - {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "True"} + { + OTEL_SEMCONV_STABILITY_OPT_IN: "gen_ai_latest_experimental", + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "SPAN_ONLY", + } ) instrumentor = AnthropicInstrumentor() instrumentor.instrument( @@ -144,9 +163,45 @@ def instrument_with_content(tracer_provider, logger_provider, meter_provider): meter_provider=meter_provider, ) - yield instrumentor - os.environ.pop(OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, None) - instrumentor.uninstrument() + try: + yield instrumentor + finally: + os.environ.pop( + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, None + ) + os.environ.pop(OTEL_SEMCONV_STABILITY_OPT_IN, None) + instrumentor.uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False + + +@pytest.fixture(scope="function") +def instrument_event_only(tracer_provider, logger_provider, meter_provider): + """Instrument Anthropic with EVENT_ONLY content capture.""" + _OpenTelemetrySemanticConventionStability._initialized = False + os.environ.update( + { + OTEL_SEMCONV_STABILITY_OPT_IN: "gen_ai_latest_experimental", + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT: "EVENT_ONLY", + OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT: "true", + } + ) + instrumentor = AnthropicInstrumentor() + instrumentor.instrument( + tracer_provider=tracer_provider, + logger_provider=logger_provider, + meter_provider=meter_provider, + ) + + try: + yield instrumentor + finally: + os.environ.pop( + OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, None + ) + os.environ.pop(OTEL_SEMCONV_STABILITY_OPT_IN, None) + os.environ.pop(OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT, None) + instrumentor.uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False @pytest.fixture @@ -230,6 +285,16 @@ def fixture_vcr(vcr): return vcr +_SCRUBBED_RESPONSE_HEADERS = frozenset( + { + "anthropic-organization-id", + } +) + + def scrub_response_headers(response): - """Scrub sensitive response headers.""" + """Scrub sensitive headers from recorded responses.""" + headers = response.get("headers", {}) + for header in _SCRUBBED_RESPONSE_HEADERS: + headers.pop(header, None) return response diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/requirements.oldest.txt b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/requirements.oldest.txt index b962a02555..1b1d2b1994 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/requirements.oldest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/requirements.oldest.txt @@ -16,14 +16,13 @@ # the oldest supported version of external dependencies. -e util/opentelemetry-util-genai -anthropic==0.16.0 -httpx>=0.25.2,<0.28.0 # Pin to version compatible with anthropic 0.16.0 (proxies arg removed in 0.28) +anthropic==0.51.0 pytest==7.4.4 pytest-vcr==1.0.2 pytest-asyncio==0.21.0 wrapt==1.16.0 -opentelemetry-api==1.37 # when updating, also update in pyproject.toml -opentelemetry-sdk==1.37 # when updating, also update in pyproject.toml -opentelemetry-semantic-conventions==0.58b0 # when updating, also update in pyproject.toml +opentelemetry-api==1.39 # when updating, also update in pyproject.toml +opentelemetry-sdk==1.39 # when updating, also update in pyproject.toml +opentelemetry-semantic-conventions==0.60b0 # when updating, also update in pyproject.toml -e instrumentation-genai/opentelemetry-instrumentation-anthropic diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_async_wrappers.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_async_wrappers.py new file mode 100644 index 0000000000..067ef0085e --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_async_wrappers.py @@ -0,0 +1,536 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from types import SimpleNamespace + +import pytest + +from opentelemetry.instrumentation.anthropic.wrappers import ( + AsyncMessagesStreamManagerWrapper, + AsyncMessagesStreamWrapper, + MessagesStreamManagerWrapper, + MessagesStreamWrapper, +) + + +def _noop_stop_llm(invocation): + del invocation + + +def _noop_fail_llm(invocation, error): + del invocation + del error + + +def _make_handler(): + return SimpleNamespace( + stop_llm=_noop_stop_llm, + fail_llm=_noop_fail_llm, + ) + + +def _make_invocation(): + return SimpleNamespace(attributes={}, request_model=None) + + +def _make_stream_wrapper(stream, handler=None): + return MessagesStreamWrapper( + stream=stream, + handler=handler or _make_handler(), + invocation=_make_invocation(), + capture_content=False, + ) + + +def _make_async_stream_wrapper(stream, handler=None): + return AsyncMessagesStreamWrapper( + stream=stream, + handler=handler or _make_handler(), + invocation=_make_invocation(), + capture_content=False, + ) + + +class _FakeSyncStream: + def __init__(self, *, events=None, error=None): + self._events = list(events or []) + self._error = error + self.close_calls = 0 + self.response = _FakeSyncResponse() + + def __iter__(self): + return self + + def __next__(self): + if self._events: + return self._events.pop(0) + if self._error is not None: + raise self._error + raise StopIteration + + def close(self): + self.close_calls += 1 + + +class _FakeAsyncStream: + def __init__(self, *, events=None, error=None): + self._events = list(events or []) + self._error = error + self.close_calls = 0 + self.final_message = SimpleNamespace(id="msg_final") + self.response = _FakeAsyncResponse() + + async def __anext__(self): + if self._events: + return self._events.pop(0) + if self._error is not None: + raise self._error + raise StopAsyncIteration + + async def close(self): + self.close_calls += 1 + + async def get_final_message(self): + return self.final_message + + +class _FakeSyncManager: + def __init__(self, stream, suppressed=False, exit_error=None): + self._stream = stream + self._suppressed = suppressed + self._exit_error = exit_error + self.exit_args = None + + def __enter__(self): + return self._stream + + def __exit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + if self._exit_error is not None: + raise self._exit_error + return self._suppressed + + +class _FakeAsyncManager: + def __init__(self, stream, suppressed=False, exit_error=None): + self._stream = stream + self._suppressed = suppressed + self._exit_error = exit_error + self.exit_args = None + + async def __aenter__(self): + return self._stream + + async def __aexit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + if self._exit_error is not None: + raise self._exit_error + return self._suppressed + + +class _FakeStreamWrapper: + def __init__(self): + self.exit_args = None + + def __exit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + return False + + +class _FakeAsyncStreamWrapper: + def __init__(self): + self.exit_args = None + + async def __aexit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + return False + + +class _FakeSyncResponse: + def __init__(self): + self.request_id = "req_sync" + self.close_calls = 0 + + def close(self): + self.close_calls += 1 + + +class _FakeAsyncResponse: + def __init__(self): + self.request_id = "req_async" + self.close_calls = 0 + self.aclose_calls = 0 + + def close(self): + self.close_calls += 1 + + async def aclose(self): + self.aclose_calls += 1 + + +def test_sync_stream_wrapper_exit_closes_without_exception(): + stream = _FakeSyncStream() + wrapper = _make_stream_wrapper(stream) + stopped = [] + + wrapper._stop = lambda: stopped.append(True) + + result = wrapper.__exit__(None, None, None) + + assert result is False + assert stream.close_calls == 1 + assert stopped == [True] + + +def test_sync_stream_wrapper_exit_fails_and_closes_on_exception(): + stream = _FakeSyncStream() + wrapper = _make_stream_wrapper(stream) + stopped = [] + failures = [] + + wrapper._stop = lambda: stopped.append(True) + wrapper._fail = lambda message, error_type: failures.append( + (message, error_type) + ) + + error = ValueError("boom") + result = wrapper.__exit__(ValueError, error, None) + + assert result is False + assert stream.close_calls == 1 + assert stopped == [True] + assert failures == [("boom", ValueError)] + + +def test_sync_stream_wrapper_processes_events_and_stops_on_completion(): + event = SimpleNamespace(type="message_start") + stream = _FakeSyncStream(events=[event]) + wrapper = _make_stream_wrapper(stream) + processed = [] + stopped = [] + + wrapper._process_chunk = processed.append + wrapper._stop = lambda: stopped.append(True) + + result = next(wrapper) + + assert result is event + assert processed == [event] + + with pytest.raises(StopIteration): + next(wrapper) + + assert stopped == [True] + + +def test_sync_stream_wrapper_fails_and_reraises_stream_errors(): + error = ValueError("boom") + stream = _FakeSyncStream(error=error) + wrapper = _make_stream_wrapper(stream) + failures = [] + + wrapper._fail = lambda message, error_type: failures.append( + (message, error_type) + ) + + with pytest.raises(ValueError, match="boom"): + next(wrapper) + + assert failures == [("boom", ValueError)] + + +def test_sync_stream_wrapper_getattr_passthrough(): + stream = _FakeSyncStream() + wrapper = _make_stream_wrapper(stream) + + assert wrapper.response.request_id == "req_sync" + + +def test_sync_stream_response_close_finalizes_wrapper(): + stream = _FakeSyncStream() + wrapper = _make_stream_wrapper(stream) + stopped = [] + + wrapper._stop = lambda: stopped.append(True) + + wrapper.response.close() + + assert stream.response.close_calls == 1 + assert stopped == [True] + + +def test_sync_manager_enter_constructs_stream_wrapper(): + stream = _FakeSyncStream() + wrapper = MessagesStreamManagerWrapper( + manager=_FakeSyncManager(stream=stream), + handler=_make_handler(), + invocation=_make_invocation(), + capture_content=False, + ) + + with wrapper as result: + assert isinstance(result, MessagesStreamWrapper) + assert result.stream is stream + assert wrapper._stream_wrapper is result + + +def test_sync_manager_exit_forwards_exception_to_stream_wrapper(): + wrapper = MessagesStreamManagerWrapper( + manager=_FakeSyncManager(stream=SimpleNamespace(), suppressed=False), + handler=SimpleNamespace(), + invocation=_make_invocation(), + capture_content=False, + ) + stream_wrapper = _FakeStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("boom") + result = wrapper.__exit__(ValueError, error, None) + + assert result is False + assert wrapper._manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) + + +def test_sync_manager_exit_uses_none_exception_when_manager_suppresses(): + wrapper = MessagesStreamManagerWrapper( + manager=_FakeSyncManager(stream=SimpleNamespace(), suppressed=True), + handler=SimpleNamespace(), + invocation=_make_invocation(), + capture_content=False, + ) + stream_wrapper = _FakeStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = RuntimeError("ignored") + result = wrapper.__exit__(RuntimeError, error, None) + + assert result is True + assert wrapper._manager.exit_args == (RuntimeError, error, None) + assert stream_wrapper.exit_args == (None, None, None) + + +def test_sync_manager_exit_still_finalizes_stream_wrapper_when_manager_raises(): + manager_error = RuntimeError("manager failure") + wrapper = MessagesStreamManagerWrapper( + manager=_FakeSyncManager( + stream=SimpleNamespace(), + suppressed=False, + exit_error=manager_error, + ), + handler=SimpleNamespace(), + invocation=_make_invocation(), + capture_content=False, + ) + stream_wrapper = _FakeStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("outer") + with pytest.raises(RuntimeError, match="manager failure"): + wrapper.__exit__(ValueError, error, None) + + assert wrapper._manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_exit_closes_without_exception(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + + wrapper._stop = lambda: stopped.append(True) + + result = await wrapper.__aexit__(None, None, None) + + assert result is False + assert stream.close_calls == 1 + assert stopped == [True] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_exit_fails_and_closes_on_exception(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + failures = [] + + wrapper._stop = lambda: stopped.append(True) + wrapper._fail = lambda message, error_type: failures.append( + (message, error_type) + ) + + error = ValueError("boom") + result = await wrapper.__aexit__(ValueError, error, None) + + assert result is False + assert stream.close_calls == 1 + assert stopped == [True] + assert failures == [("boom", ValueError)] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_close_uses_close_and_stops(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + + wrapper._stop = lambda: stopped.append(True) + + await wrapper.close() + + assert stream.close_calls == 1 + assert stopped == [True] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_processes_events_and_stops_on_completion(): + event = SimpleNamespace(type="message_start") + stream = _FakeAsyncStream(events=[event]) + wrapper = _make_async_stream_wrapper(stream) + processed = [] + stopped = [] + + wrapper._process_chunk = processed.append + wrapper._stop = lambda: stopped.append(True) + + result = await anext(wrapper) + + assert result is event + assert processed == [event] + + with pytest.raises(StopAsyncIteration): + await anext(wrapper) + + assert stopped == [True] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_fails_and_reraises_stream_errors(): + error = ValueError("boom") + stream = _FakeAsyncStream(error=error) + wrapper = _make_async_stream_wrapper(stream) + failures = [] + + wrapper._fail = lambda message, error_type: failures.append( + (message, error_type) + ) + + with pytest.raises(ValueError, match="boom"): + await anext(wrapper) + + assert failures == [("boom", ValueError)] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_preserves_stream_helper_methods(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + + result = await wrapper.get_final_message() + + assert result is stream.final_message + assert wrapper.response.request_id == "req_async" + + +@pytest.mark.asyncio +async def test_async_stream_response_aclose_finalizes_wrapper(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + + wrapper._stop = lambda: stopped.append(True) + + await wrapper.response.aclose() + + assert stream.response.aclose_calls == 1 + assert stopped == [True] + + +@pytest.mark.asyncio +async def test_async_manager_enter_constructs_async_stream_wrapper(): + stream = _FakeAsyncStream() + wrapper = AsyncMessagesStreamManagerWrapper( + manager=_FakeAsyncManager(stream=stream), + handler=_make_handler(), + invocation=_make_invocation(), + capture_content=False, + ) + + async with wrapper as result: + assert isinstance(result, AsyncMessagesStreamWrapper) + assert result.stream is stream + assert wrapper._stream_wrapper is result + + +@pytest.mark.asyncio +async def test_async_manager_exit_forwards_exception_to_stream_wrapper(): + wrapper = AsyncMessagesStreamManagerWrapper( + manager=_FakeAsyncManager(stream=SimpleNamespace(), suppressed=False), + handler=SimpleNamespace(), + invocation=_make_invocation(), + capture_content=False, + ) + stream_wrapper = _FakeAsyncStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("boom") + result = await wrapper.__aexit__(ValueError, error, None) + + assert result is False + assert wrapper._manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) + + +@pytest.mark.asyncio +async def test_async_manager_exit_uses_none_exception_when_manager_suppresses(): + wrapper = AsyncMessagesStreamManagerWrapper( + manager=_FakeAsyncManager(stream=SimpleNamespace(), suppressed=True), + handler=SimpleNamespace(), + invocation=_make_invocation(), + capture_content=False, + ) + stream_wrapper = _FakeAsyncStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = RuntimeError("ignored") + result = await wrapper.__aexit__(RuntimeError, error, None) + + assert result is True + assert wrapper._manager.exit_args == (RuntimeError, error, None) + assert stream_wrapper.exit_args == (None, None, None) + + +@pytest.mark.asyncio +async def test_async_manager_exit_still_finalizes_stream_wrapper_when_manager_raises(): + manager_error = RuntimeError("manager failure") + wrapper = AsyncMessagesStreamManagerWrapper( + manager=_FakeAsyncManager( + stream=SimpleNamespace(), + suppressed=False, + exit_error=manager_error, + ), + handler=SimpleNamespace(), + invocation=_make_invocation(), + capture_content=False, + ) + stream_wrapper = _FakeAsyncStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("outer") + with pytest.raises(RuntimeError, match="manager failure"): + await wrapper.__aexit__(ValueError, error, None) + + assert wrapper._manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) diff --git a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_sync_messages.py b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_sync_messages.py index f656f61eef..a073f3b569 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_sync_messages.py +++ b/instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/test_sync_messages.py @@ -14,10 +14,23 @@ """Tests for sync Messages.create instrumentation.""" +import inspect +import json +import os +from pathlib import Path + import pytest from anthropic import Anthropic, APIConnectionError, NotFoundError +from anthropic.resources.messages import Messages as _Messages from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor +from opentelemetry.instrumentation.anthropic.messages_extractors import ( + GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, + GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, +) +from opentelemetry.instrumentation.anthropic.wrappers import ( + MessagesStreamWrapper, +) from opentelemetry.semconv._incubating.attributes import ( error_attributes as ErrorAttributes, ) @@ -28,8 +41,32 @@ server_attributes as ServerAttributes, ) +# Detect whether the installed anthropic SDK supports tools / thinking params. +# Older SDK versions (e.g. 0.16.0) do not accept these keyword arguments. +_create_params = set(inspect.signature(_Messages.create).parameters) +_has_tools_param = "tools" in _create_params +_has_thinking_param = "thinking" in _create_params + + +def normalize_stop_reason(stop_reason): + """Map Anthropic stop reasons to GenAI semconv values.""" + return { + "end_turn": "stop", + "stop_sequence": "stop", + "max_tokens": "length", + "tool_use": "tool_calls", + }.get(stop_reason, stop_reason) -def assert_span_attributes( + +def expected_input_tokens(usage): + """Compute semconv input tokens from Anthropic usage.""" + base = getattr(usage, "input_tokens", 0) or 0 + cache_creation = getattr(usage, "cache_creation_input_tokens", 0) or 0 + cache_read = getattr(usage, "cache_read_input_tokens", 0) or 0 + return base + cache_creation + cache_read + + +def assert_span_attributes( # pylint: disable=too-many-arguments span, request_model, response_id=None, @@ -86,6 +123,27 @@ def assert_span_attributes( ) +def _load_span_messages(span, attribute): + value = span.attributes.get(attribute) + assert value is not None + assert isinstance(value, str) + parsed = json.loads(value) + assert isinstance(parsed, list) + return parsed + + +def _skip_if_cassette_missing_and_no_real_key(request): + cassette_path = ( + Path(__file__).parent / "cassettes" / f"{request.node.name}.yaml" + ) + api_key = os.getenv("ANTHROPIC_API_KEY") + if not cassette_path.exists() and api_key == "test_anthropic_api_key": + pytest.skip( + f"Cassette {cassette_path.name} is missing. " + "Set a real ANTHROPIC_API_KEY to record it." + ) + + @pytest.mark.vcr() def test_sync_messages_create_basic( span_exporter, anthropic_client, instrument_no_content @@ -108,11 +166,42 @@ def test_sync_messages_create_basic( request_model=model, response_id=response.id, response_model=response.model, - input_tokens=response.usage.input_tokens, + input_tokens=expected_input_tokens(response.usage), output_tokens=response.usage.output_tokens, - finish_reasons=[response.stop_reason], + finish_reasons=[normalize_stop_reason(response.stop_reason)], + ) + + +@pytest.mark.vcr() +def test_sync_messages_create_captures_content( + span_exporter, anthropic_client, instrument_with_content +): + """Test content capture on non-streaming create.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + input_messages = _load_span_messages( + span, GenAIAttributes.GEN_AI_INPUT_MESSAGES + ) + output_messages = _load_span_messages( + span, GenAIAttributes.GEN_AI_OUTPUT_MESSAGES ) + assert input_messages[0]["role"] == "user" + assert input_messages[0]["parts"][0]["type"] == "text" + assert output_messages[0]["role"] == "assistant" + assert output_messages[0]["parts"][0]["type"] == "text" + @pytest.mark.vcr() def test_sync_messages_create_with_all_params( @@ -165,10 +254,9 @@ def test_sync_messages_create_token_usage( span = spans[0] assert GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS in span.attributes assert GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS in span.attributes - assert ( - span.attributes[GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS] - == response.usage.input_tokens - ) + assert span.attributes[ + GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS + ] == expected_input_tokens(response.usage) assert ( span.attributes[GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS] == response.usage.output_tokens @@ -195,7 +283,7 @@ def test_sync_messages_create_stop_reason( span = spans[0] # Anthropic's stop_reason should be wrapped in a tuple (OTel converts lists) assert span.attributes[GenAIAttributes.GEN_AI_RESPONSE_FINISH_REASONS] == ( - response.stop_reason, + normalize_stop_reason(response.stop_reason), ) @@ -300,3 +388,554 @@ def test_multiple_instrument_uninstrument_cycles( meter_provider=meter_provider, ) instrumentor.uninstrument() + + +@pytest.mark.vcr() +def test_sync_messages_create_streaming( # pylint: disable=too-many-locals + span_exporter, anthropic_client, instrument_no_content +): + """Test streaming message creation produces correct span.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + # Collect response data from stream + response_text = "" + response_id = None + response_model = None + stop_reason = None + input_tokens = None + output_tokens = None + + with anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) as stream: + for chunk in stream: + # Extract data from chunks for assertion + if chunk.type == "message_start": + message = getattr(chunk, "message", None) + if message: + response_id = getattr(message, "id", None) + response_model = getattr(message, "model", None) + usage = getattr(message, "usage", None) + if usage: + input_tokens = getattr(usage, "input_tokens", None) + elif chunk.type == "content_block_delta": + delta = getattr(chunk, "delta", None) + if delta and hasattr(delta, "text"): + response_text += delta.text + elif chunk.type == "message_delta": + delta = getattr(chunk, "delta", None) + if delta: + stop_reason = getattr(delta, "stop_reason", None) + usage = getattr(chunk, "usage", None) + if usage: + output_tokens = getattr(usage, "output_tokens", None) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + + assert_span_attributes( + spans[0], + request_model=model, + response_id=response_id, + response_model=response_model, + input_tokens=input_tokens, + output_tokens=output_tokens, + finish_reasons=[normalize_stop_reason(stop_reason)] + if stop_reason + else None, + ) + + +@pytest.mark.vcr() +def test_sync_messages_create_streaming_captures_content( + span_exporter, anthropic_client, instrument_with_content +): + """Test content capture on create(stream=True).""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + with anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) as stream: + for _ in stream: + pass + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + input_messages = _load_span_messages( + span, GenAIAttributes.GEN_AI_INPUT_MESSAGES + ) + output_messages = _load_span_messages( + span, GenAIAttributes.GEN_AI_OUTPUT_MESSAGES + ) + assert input_messages[0]["role"] == "user" + assert output_messages[0]["role"] == "assistant" + assert output_messages[0]["parts"] + + +@pytest.mark.vcr() +def test_sync_messages_create_streaming_iteration( + span_exporter, anthropic_client, instrument_no_content +): + """Test streaming with direct iteration (without context manager).""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hi."}] + + stream = anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) + + # Consume the stream by iterating + chunks = list(stream) + assert len(chunks) > 0 + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + + span = spans[0] + assert span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] == model + # Verify span has response attributes from streaming + assert GenAIAttributes.GEN_AI_RESPONSE_ID in span.attributes + assert GenAIAttributes.GEN_AI_RESPONSE_MODEL in span.attributes + + +@pytest.mark.vcr() +def test_sync_messages_create_streaming_delegates_response_attribute( + request, anthropic_client, instrument_no_content +): + """Stream wrapper should expose attributes from the wrapped stream.""" + _skip_if_cassette_missing_and_no_real_key(request) + + stream = anthropic_client.messages.create( + model="claude-sonnet-4-20250514", + max_tokens=100, + messages=[{"role": "user", "content": "Say hi."}], + stream=True, + ) + + # `response` exists on Anthropic Stream and should be reachable on wrapper. + assert stream.response is not None + assert stream.response.status_code == 200 + assert stream.response.headers.get("request-id") is not None + stream.close() + + +def test_sync_messages_create_streaming_connection_error( + span_exporter, instrument_no_content +): + """Test that connection errors during streaming are handled correctly.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Hello"}] + + # Create client with invalid endpoint + client = Anthropic(base_url="http://localhost:9999") + + with pytest.raises(APIConnectionError): + client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + timeout=0.1, + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + + span = spans[0] + assert span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] == model + assert ErrorAttributes.ERROR_TYPE in span.attributes + assert "APIConnectionError" in span.attributes[ErrorAttributes.ERROR_TYPE] + + +@pytest.mark.vcr() +@pytest.mark.skipif( + not _has_tools_param, + reason="anthropic SDK too old to support 'tools' parameter", +) +def test_sync_messages_create_captures_tool_use_content( + request, span_exporter, anthropic_client, instrument_with_content +): + """Test that tool_use blocks are captured as tool_call parts.""" + _skip_if_cassette_missing_and_no_real_key(request) + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "What is the weather in SF?"}] + + anthropic_client.messages.create( + model=model, + max_tokens=256, + messages=messages, + tools=[ + { + "name": "get_weather", + "description": "Get weather by city", + "input_schema": { + "type": "object", + "properties": {"city": {"type": "string"}}, + "required": ["city"], + }, + } + ], + tool_choice={"type": "tool", "name": "get_weather"}, + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + output_messages = _load_span_messages( + span, GenAIAttributes.GEN_AI_OUTPUT_MESSAGES + ) + + assert any( + part.get("type") == "tool_call" + for message in output_messages + for part in message.get("parts", []) + ) + + +@pytest.mark.vcr() +@pytest.mark.skipif( + not _has_thinking_param, + reason="anthropic SDK too old to support 'thinking' parameter", +) +def test_sync_messages_create_captures_thinking_content( + request, span_exporter, anthropic_client, instrument_with_content +): + """Test that thinking blocks are captured as reasoning parts.""" + _skip_if_cassette_missing_and_no_real_key(request) + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "What is 17*19? Think first."}] + + anthropic_client.messages.create( + model=model, + max_tokens=16000, + messages=messages, + thinking={"type": "enabled", "budget_tokens": 10000}, + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + output_messages = _load_span_messages( + span, GenAIAttributes.GEN_AI_OUTPUT_MESSAGES + ) + + assert any( + part.get("type") == "reasoning" + for message in output_messages + for part in message.get("parts", []) + ) + + +@pytest.mark.vcr() +def test_stream_wrapper_finalize_idempotent( # pylint: disable=too-many-locals + span_exporter, + anthropic_client, + instrument_no_content, +): + """Fully consumed stream plus explicit close should still yield one span.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + stream = anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) + + response_id = None + response_model = None + stop_reason = None + input_tokens = None + output_tokens = None + + # Consume the stream fully, then call close() to verify idempotent finalization. + for chunk in stream: + if chunk.type == "message_start": + message = getattr(chunk, "message", None) + if message: + response_id = getattr(message, "id", None) + response_model = getattr(message, "model", None) + usage = getattr(message, "usage", None) + if usage: + input_tokens = expected_input_tokens(usage) + elif chunk.type == "message_delta": + delta = getattr(chunk, "delta", None) + if delta: + stop_reason = getattr(delta, "stop_reason", None) + usage = getattr(chunk, "usage", None) + if usage: + output_tokens = getattr(usage, "output_tokens", None) + input_tokens = expected_input_tokens(usage) + + stream.close() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + assert_span_attributes( + spans[0], + request_model=model, + response_id=response_id, + response_model=response_model, + input_tokens=input_tokens, + output_tokens=output_tokens, + finish_reasons=[normalize_stop_reason(stop_reason)] + if stop_reason + else None, + ) + + +@pytest.mark.vcr() +def test_sync_messages_create_aggregates_cache_tokens( + span_exporter, anthropic_client, instrument_no_content +): + """Non-streaming response with non-zero cache tokens aggregates correctly.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + response = anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + assert GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS in span.attributes + assert GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS in span.attributes + assert span.attributes[ + GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS + ] == expected_input_tokens(response.usage) + assert ( + span.attributes[GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS] + == response.usage.output_tokens + ) + cache_creation = getattr(response.usage, "cache_creation_input_tokens", 0) + cache_read = getattr(response.usage, "cache_read_input_tokens", 0) + assert ( + span.attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS] + == cache_creation + ) + assert span.attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] == cache_read + + +@pytest.mark.vcr() +def test_sync_messages_create_streaming_aggregates_cache_tokens( + span_exporter, anthropic_client, instrument_no_content +): + """Streaming response with non-zero cache tokens aggregates correctly.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + input_tokens = None + output_tokens = None + cache_creation = None + cache_read = None + + with anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) as stream: + for chunk in stream: + if chunk.type == "message_delta": + usage = getattr(chunk, "usage", None) + if usage: + input_tokens = expected_input_tokens(usage) + output_tokens = getattr(usage, "output_tokens", None) + cache_creation = getattr( + usage, "cache_creation_input_tokens", None + ) + cache_read = getattr( + usage, "cache_read_input_tokens", None + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + assert GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS in span.attributes + assert GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS in span.attributes + assert ( + span.attributes[GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS] + == input_tokens + ) + assert ( + span.attributes[GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS] + == output_tokens + ) + assert ( + span.attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS] + == cache_creation + ) + assert span.attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] == cache_read + + +@pytest.mark.vcr() +def test_sync_messages_create_stream_propagation_error( + span_exporter, anthropic_client, instrument_no_content, monkeypatch +): + """Mid-stream errors from the underlying iterator must propagate and record error on span.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + stream = anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) + + # Wrap the underlying stream so we inject an iteration error but still + # delegate close()/other behavior to the real stream. + class ErrorInjectingStreamDelegate: + def __init__(self, inner): + self._inner = inner + self._count = 0 + + def __iter__(self): + return self + + def __next__(self): + # Fail after yielding one chunk so this exercises a mid-stream error. + if self._count == 1: + raise ConnectionError("connection reset during stream") + self._count += 1 + return next(self._inner) + + def close(self): + return self._inner.close() + + def __getattr__(self, name): + return getattr(self._inner, name) + + monkeypatch.setattr( + stream, "stream", ErrorInjectingStreamDelegate(stream.stream) + ) + + with pytest.raises( + ConnectionError, match="connection reset during stream" + ): + with stream: + for _ in stream: + pass + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + assert span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] == model + assert span.attributes[ErrorAttributes.ERROR_TYPE] == "ConnectionError" + + +@pytest.mark.vcr() +def test_sync_messages_create_streaming_user_exception( + span_exporter, anthropic_client, instrument_no_content +): + """Test that user raised exceptions are propagated.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + with pytest.raises(ValueError, match="User raised exception"): + with anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) as stream: + for _ in stream: + raise ValueError("User raised exception") + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + assert span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] == model + assert span.attributes[ErrorAttributes.ERROR_TYPE] == "ValueError" + + +@pytest.mark.vcr() +def test_sync_messages_create_instrumentation_error_swallowed( + span_exporter, anthropic_client, instrument_no_content, monkeypatch +): + """Instrumentation errors in _process_chunk must not propagate to user code.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + def exploding_process_chunk(self, chunk): + raise RuntimeError("instrumentation bug") + + monkeypatch.setattr( + MessagesStreamWrapper, "_process_chunk", exploding_process_chunk + ) + + with anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + stream=True, + ) as stream: + chunks = list(stream) + + assert len(chunks) > 0 + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + assert span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] == model + assert ErrorAttributes.ERROR_TYPE not in span.attributes + + +# ============================================================================= +# Tests for EVENT_ONLY content capture mode +# ============================================================================= + + +@pytest.mark.vcr() +def test_sync_messages_create_event_only_no_content_in_span( + span_exporter, log_exporter, anthropic_client, instrument_event_only +): + """Test that EVENT_ONLY mode does not capture content in span attributes + but does emit a log event with the content.""" + model = "claude-sonnet-4-20250514" + messages = [{"role": "user", "content": "Say hello in one word."}] + + anthropic_client.messages.create( + model=model, + max_tokens=100, + messages=messages, + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + # Content should NOT be in span attributes under EVENT_ONLY + assert GenAIAttributes.GEN_AI_INPUT_MESSAGES not in span.attributes + assert GenAIAttributes.GEN_AI_OUTPUT_MESSAGES not in span.attributes + + # Basic span attributes should still be present + assert span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] == model + assert GenAIAttributes.GEN_AI_RESPONSE_MODEL in span.attributes + assert GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS in span.attributes + assert GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS in span.attributes + + # A log event should have been emitted with the content + logs = log_exporter.get_finished_logs() + assert len(logs) == 1 + log_record = logs[0].log_record + assert log_record.event_name == "gen_ai.client.inference.operation.details" diff --git a/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/pyproject.toml index 3149629fac..fb38e61589 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/pyproject.toml @@ -24,16 +24,14 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] dependencies = [ - "opentelemetry-api ~= 1.37", - "opentelemetry-instrumentation ~= 0.58b0", - "opentelemetry-semantic-conventions ~= 0.58b0", + "opentelemetry-api ~= 1.39", + "opentelemetry-instrumentation ~= 0.60b0", + "opentelemetry-semantic-conventions ~= 0.60b0", "opentelemetry-util-genai >= 0.2b0, <0.4b0", ] [project.optional-dependencies] -instruments = [ - "claude-agent-sdk >= 0.1.14", -] +instruments = ["claude-agent-sdk >= 0.1.14"] [project.entry-points.opentelemetry_instrumentor] claude-agent-sdk = "opentelemetry.instrumentation.claude_agent_sdk:ClaudeAgentSDKInstrumentor" @@ -46,11 +44,7 @@ Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib" path = "src/opentelemetry/instrumentation/claude_agent_sdk/version.py" [tool.hatch.build.targets.sdist] -include = [ - "/src", - "/tests", - "/examples", -] +include = ["/src", "/tests", "/examples"] [tool.hatch.build.targets.wheel] packages = ["src/opentelemetry"] diff --git a/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/tests/requirements.oldest.txt b/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/tests/requirements.oldest.txt index adfadef283..833e05fb1d 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/tests/requirements.oldest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk/tests/requirements.oldest.txt @@ -21,8 +21,8 @@ pytest==7.4.4 pytest-vcr==1.0.2 pytest-asyncio==0.21.0 wrapt==1.16.0 -opentelemetry-api==1.37 # when updating, also update in pyproject.toml -opentelemetry-sdk==1.37 # when updating, also update in pyproject.toml -opentelemetry-semantic-conventions==0.58b0 # when updating, also update in pyproject.toml +opentelemetry-api==1.39 # when updating, also update in pyproject.toml +opentelemetry-sdk==1.39 # when updating, also update in pyproject.toml +opentelemetry-semantic-conventions==0.60b0 # when updating, also update in pyproject.toml -e instrumentation-genai/opentelemetry-instrumentation-claude-agent-sdk diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-google-genai/pyproject.toml index a63b28d8b4..2ea01de4f3 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/pyproject.toml @@ -22,7 +22,7 @@ dynamic = ["version"] description = "OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -39,10 +39,10 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-api ~=1.37", - "opentelemetry-instrumentation >=0.58b0, <2", - "opentelemetry-semantic-conventions >=0.58b0, <2", - "opentelemetry-util-genai >= 0.3b0, <0.4b0", + "opentelemetry-api ~=1.39", + "opentelemetry-instrumentation >=0.60b0, <2", + "opentelemetry-semantic-conventions >=0.60b0, <2", + "opentelemetry-util-genai >= 0.4b0.dev, <0.5b0", ] [project.optional-dependencies] @@ -70,4 +70,4 @@ exclude = ["**/__pycache__"] stubPath = "types" reportMissingImports = "error" reportMissingTypeStubs = false -pythonVersion = "3.9" +pythonVersion = "3.10" diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/message.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/message.py index 29ef112a6f..a7349f0c83 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/message.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/message.py @@ -15,20 +15,20 @@ from __future__ import annotations import logging -from dataclasses import dataclass from enum import Enum -from typing import Literal from google.genai import types as genai_types from opentelemetry.util.genai.types import ( + Blob, FinishReason, InputMessage, MessagePart, OutputMessage, Text, - ToolCall, + ToolCallRequest, ToolCallResponse, + Uri, ) @@ -39,23 +39,6 @@ class Role(str, Enum): TOOL = "tool" -@dataclass -class BlobPart: - data: bytes - mime_type: str - type: Literal["blob"] = "blob" - - -@dataclass -class FileDataPart: - mime_type: str - uri: str - type: Literal["file_data"] = "file_data" - - class Config: - extra = "allow" - - _logger = logging.getLogger(__name__) @@ -121,16 +104,26 @@ def tool_call_id(name: str | None) -> str: if (text := part.text) is not None: return Text(content=text) - if data := part.inline_data: - return BlobPart(mime_type=data.mime_type or "", data=data.data or b"") + if inline_data := part.inline_data: + mime_type = inline_data.mime_type or "" + modality = mime_type.split("/")[0] if mime_type else "" + return Blob( + mime_type=mime_type, + modality=modality, + content=inline_data.data or b"", + ) - if data := part.file_data: - return FileDataPart( - mime_type=data.mime_type or "", uri=data.file_uri or "" + if file_data := part.file_data: + mime_type = file_data.mime_type or "" + modality = mime_type.split("/")[0] if mime_type else "" + return Uri( + mime_type=mime_type, + modality=modality, + uri=file_data.file_uri or "", ) if call := part.function_call: - return ToolCall( + return ToolCallRequest( id=call.id or tool_call_id(call.name), name=call.name or "", arguments=call.args, diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py index ebaeeb8748..070939bb14 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py @@ -124,8 +124,7 @@ def _create_stream_mock(self, e=None): mock = unittest.mock.MagicMock() def _default_impl(*args, **kwargs): - for response in self._responses: - yield response + yield from self._responses if not e: mock.side_effect = _default_impl diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/test_e2e.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/test_e2e.py index 47abf4b877..c29fe28bd1 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/test_e2e.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/test_e2e.py @@ -24,11 +24,9 @@ output is the purview of the other tests in this directory.""" import asyncio -import gzip import json import os import subprocess -import sys import time import fsspec @@ -258,51 +256,6 @@ def _convert_body_to_literal(data): return data -# Helper for enforcing GZIP compression where it was originally. -def _ensure_gzip_single_response(data: bytes): - try: - # Attempt to decompress, first, to avoid double compression. - gzip.decompress(data) - return data - except gzip.BadGzipFile: - # It must not have been compressed in the first place. - return gzip.compress(data) - - -# VCRPy automatically decompresses responses before saving them, but it may forget to -# re-encode them when the data is loaded. This can create issues with decompression. -# This is why we re-encode on load; to accurately replay what was originally sent. -# -# https://vcrpy.readthedocs.io/en/latest/advanced.html#decode-compressed-response -def _ensure_casette_gzip(loaded_casette): - for interaction in loaded_casette["interactions"]: - response = interaction["response"] - headers = response["headers"] - if ( - "content-encoding" not in headers - and "Content-Encoding" not in headers - ): - continue - if ( - "content-encoding" in headers - and "gzip" not in headers["content-encoding"] - ): - continue - if ( - "Content-Encoding" in headers - and "gzip" not in headers["Content-Encoding"] - ): - continue - response["body"]["string"] = _ensure_gzip_single_response( - response["body"]["string"].encode() - ) - - -def _maybe_ensure_casette_gzip(result): - if sys.version_info[0] == 3 and sys.version_info[1] == 9: - _ensure_casette_gzip(result) - - class _PrettyPrintJSONBody: """This makes request and response body recordings more readable.""" @@ -315,9 +268,7 @@ def serialize(cassette_dict): @staticmethod def deserialize(cassette_string): - result = yaml.load(cassette_string, Loader=yaml.Loader) - _maybe_ensure_casette_gzip(result) - return result + return yaml.load(cassette_string, Loader=yaml.Loader) @pytest.fixture(name="fully_initialized_vcr", scope="module", autouse=True) diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/requirements.oldest.txt b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/requirements.oldest.txt index 357bbeccb3..092a723ec4 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/requirements.oldest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/requirements.oldest.txt @@ -21,11 +21,11 @@ pytest-vcr==1.0.2 google-auth==2.15.0 google-genai==1.32.0 -opentelemetry-api==1.37.0 -opentelemetry-sdk==1.37.0 -opentelemetry-semantic-conventions==0.58b0 -opentelemetry-instrumentation==0.58b0 -opentelemetry-util-genai[upload]==0.3b0 +opentelemetry-api==1.39.0 +opentelemetry-sdk==1.39.0 +opentelemetry-semantic-conventions==0.60b0 +opentelemetry-instrumentation==0.60b0 +-e util/opentelemetry-util-genai[upload] fsspec==2025.9.0 diff --git a/instrumentation-genai/opentelemetry-instrumentation-langchain/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-langchain/pyproject.toml index 13947d2555..80a406b7da 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-langchain/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-langchain/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Official Langchain instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/instrumentation-genai/opentelemetry-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/callback_handler.py b/instrumentation-genai/opentelemetry-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/callback_handler.py index 8f642567ca..5235af3c7f 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/callback_handler.py +++ b/instrumentation-genai/opentelemetry-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/callback_handler.py @@ -14,7 +14,7 @@ from __future__ import annotations -from typing import Any, Optional +from typing import Any, Optional, cast from uuid import UUID from langchain_core.callbacks import BaseCallbackHandler @@ -28,7 +28,8 @@ from opentelemetry.util.genai.types import ( Error, InputMessage, - LLMInvocation, + LLMInvocation, # TODO: migrate to InferenceInvocation + MessagePart, OutputMessage, Text, ) @@ -133,7 +134,11 @@ def on_chat_model_start( Text(content=text_value, type="text") ) - input_messages.append(InputMessage(parts=parts, role=role)) + input_messages.append( + InputMessage( + parts=cast(list[MessagePart], parts), role=role + ) + ) llm_invocation = LLMInvocation( request_model=request_model, @@ -153,7 +158,7 @@ def on_chat_model_start( self._invocation_manager.add_invocation_state( run_id=run_id, parent_run_id=parent_run_id, - invocation=llm_invocation, + invocation=llm_invocation, # pyright: ignore[reportArgumentType] ) def on_llm_end( @@ -166,7 +171,8 @@ def on_llm_end( ) -> None: llm_invocation = self._invocation_manager.get_invocation(run_id=run_id) if llm_invocation is None or not isinstance( - llm_invocation, LLMInvocation + llm_invocation, + LLMInvocation, ): # If the invocation does not exist, we cannot set attributes or end it return @@ -206,7 +212,7 @@ def on_llm_end( role = chat_generation.message.type output_message = OutputMessage( role=role, - parts=parts, + parts=cast(list[MessagePart], parts), finish_reason=finish_reason, ) output_messages.append(output_message) @@ -257,7 +263,8 @@ def on_llm_error( ) -> None: llm_invocation = self._invocation_manager.get_invocation(run_id=run_id) if llm_invocation is None or not isinstance( - llm_invocation, LLMInvocation + llm_invocation, + LLMInvocation, ): # If the invocation does not exist, we cannot set attributes or end it return diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/CHANGELOG.md b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/CHANGELOG.md index 95f69d6ded..90f8c11e01 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/CHANGELOG.md +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/CHANGELOG.md @@ -6,8 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +- Align AgentSpanData test stubs and span processor with real OpenAI Agents SDK; + remove non-existent `operation`, `description`, `agent_id`, and `model` fields. + ([#4229](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4229)) - Document official package metadata and README for the OpenAI Agents instrumentation. ([#3859](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3859)) +- Populate instructions and tool definitions from Response obj. + ([#4196](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4196)) ## Version 0.1.0 (2025-10-15) diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/pyproject.toml index 61a63e60bf..fe7bfa6d82 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Official OpenAI Agents instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,10 +25,10 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-api >= 1.37", + "opentelemetry-api >= 1.39", "opentelemetry-instrumentation >= 0.58b0", - "opentelemetry-semantic-conventions >= 0.58b0", - "opentelemetry-util-genai" + "opentelemetry-semantic-conventions >= 0.60b0", + "opentelemetry-util-genai >= 0.4b0.dev" ] [project.optional-dependencies] diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py index d1dce8ec5e..55539a4931 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py @@ -426,6 +426,7 @@ def get_span_name( class GenAISemanticProcessor(TracingProcessor): """Trace processor adding GenAI semantic convention attributes with metrics.""" + # pylint: disable=too-many-positional-arguments def __init__( self, tracer: Optional[Tracer] = None, @@ -1056,10 +1057,20 @@ def _build_content_payload(self, span: Span[Any]) -> ContentPayload: elif _is_instance_of(span_data, ResponseSpanData): span_input = getattr(span_data, "input", None) + response_obj = getattr(span_data, "response", None) if capture_messages and span_input: payload.input_messages = ( self._normalize_messages_to_role_parts(span_input) ) + + if ( + capture_system + and response_obj + and hasattr(response_obj, "instructions") + ): + payload.system_instructions = self._normalize_to_text_parts( + response_obj.instructions + ) if capture_system and span_input: sys_instr = self._collect_system_instructions(span_input) if sys_instr: @@ -1519,17 +1530,8 @@ def _get_operation_name(self, span_data: Any) -> str: return GenAIOperationName.CHAT return GenAIOperationName.TEXT_COMPLETION if _is_instance_of(span_data, AgentSpanData): - # Could be create_agent or invoke_agent based on context - operation = getattr(span_data, "operation", None) - normalized = ( - operation.strip().lower() - if isinstance(operation, str) - else None - ) - if normalized in {"create", "create_agent"}: - return GenAIOperationName.CREATE_AGENT - if normalized in {"invoke", "invoke_agent"}: - return GenAIOperationName.INVOKE_AGENT + # The OpenAI Agents SDK AgentSpanData has no "operation" field; + # agent spans always represent invoke_agent. return GenAIOperationName.INVOKE_AGENT if _is_instance_of(span_data, FunctionSpanData): return GenAIOperationName.EXECUTE_TOOL @@ -1831,24 +1833,20 @@ def _get_attributes_from_agent_span_data( if name: yield GEN_AI_AGENT_NAME, name - agent_id = ( - self.agent_id - or getattr(span_data, "agent_id", None) - or self._agent_id_default - ) + # agent_id and description are not available on the OpenAI Agents SDK + # AgentSpanData; only use user-configured overrides. + agent_id = self.agent_id or self._agent_id_default if agent_id: yield GEN_AI_AGENT_ID, agent_id - description = ( - self.agent_description - or getattr(span_data, "description", None) - or self._agent_description_default - ) + description = self.agent_description or self._agent_description_default if description: yield GEN_AI_AGENT_DESCRIPTION, description - model = getattr(span_data, "model", None) - if not model and agent_content: + # The OpenAI Agents SDK AgentSpanData has no "model" field; fall back to + # the model aggregated from child generation/response spans. + model = None + if agent_content: model = agent_content.get("request_model") if model: yield GEN_AI_REQUEST_MODEL, model @@ -2029,6 +2027,22 @@ def _get_attributes_from_response_span_data( if output_tokens is not None: yield GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens + # Tool definitions from response + if self._capture_tool_definitions and hasattr( + span_data.response, "tools" + ): + yield ( + GEN_AI_TOOL_DEFINITIONS, + safe_json_dumps( + list( + map( + lambda tool: tool.to_dict(), + span_data.response.tools, + ) + ) + ), + ) + # Input/output messages if ( self.include_sensitive_data diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.latest.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.latest.txt index e224e4349b..2bd34ba349 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.latest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.latest.txt @@ -50,4 +50,5 @@ grpcio>=1.60.0; implementation_name != "pypy" grpcio<1.60.0; implementation_name == "pypy" -e opentelemetry-instrumentation +-e util/opentelemetry-util-genai -e instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.oldest.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.oldest.txt index 2e935a63b6..537c2b9ba2 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.oldest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.oldest.txt @@ -26,10 +26,11 @@ pytest-asyncio==0.21.0 wrapt==1.16.0 opentelemetry-exporter-otlp-proto-grpc~=1.30 opentelemetry-exporter-otlp-proto-http~=1.30 -opentelemetry-api==1.37 # when updating, also update in pyproject.toml -opentelemetry-sdk==1.37 # when updating, also update in pyproject.toml -opentelemetry-semantic-conventions==0.58b0 # when updating, also update in pyproject.toml +opentelemetry-api==1.39 # when updating, also update in pyproject.toml +opentelemetry-sdk==1.39 # when updating, also update in pyproject.toml +opentelemetry-semantic-conventions==0.60b0 # when updating, also update in pyproject.toml grpcio>=1.60.0; implementation_name != "pypy" grpcio<1.60.0; implementation_name == "pypy" +-e util/opentelemetry-util-genai -e instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/stubs/agents/tracing/__init__.py b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/stubs/agents/tracing/__init__.py index 4ed06c8977..509fd537b3 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/stubs/agents/tracing/__init__.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/stubs/agents/tracing/__init__.py @@ -35,12 +35,9 @@ @dataclass class AgentSpanData: name: str | None = None + handoffs: list[str] | None = None tools: list[str] | None = None output_type: str | None = None - description: str | None = None - agent_id: str | None = None - model: str | None = None - operation: str | None = None @property def type(self) -> str: @@ -200,8 +197,16 @@ def generation_span(**kwargs: Any): @contextmanager -def agent_span(**kwargs: Any): - data = AgentSpanData(**kwargs) +def agent_span( + name: str, + handoffs: list[str] | None = None, + tools: list[str] | None = None, + output_type: str | None = None, + **kwargs: Any, +): + data = AgentSpanData( + name=name, handoffs=handoffs, tools=tools, output_type=output_type + ) span = _PROVIDER.create_span(data, parent=_CURRENT_TRACE) span.start() try: diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_tracer.py b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_tracer.py index 1f21ab25c0..5c62fd492e 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_tracer.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_tracer.py @@ -25,6 +25,7 @@ set_trace_processors, trace, ) +from openai.types.responses import FunctionTool # noqa: E402 from opentelemetry.instrumentation.openai_agents import ( # noqa: E402 OpenAIAgentsInstrumentor, @@ -62,6 +63,9 @@ GEN_AI_OUTPUT_MESSAGES = getattr( GenAI, "GEN_AI_OUTPUT_MESSAGES", "gen_ai.output.messages" ) +GEN_AI_TOOL_DEFINITIONS = getattr( + GenAI, "GEN_AI_TOOL_DEFINITIONS", "gen_ai.tool.definitions" +) def _instrument_with_provider(**instrument_kwargs): @@ -171,40 +175,31 @@ def test_function_span_records_tool_attributes(): exporter.clear() -def test_agent_create_span_records_attributes(): +def test_agent_invoke_span_records_attributes(): instrumentor, exporter = _instrument_with_provider() try: with trace("workflow"): with agent_span( - operation="create", name="support_bot", - description="Answers support questions", - agent_id="agt_123", - model="gpt-4o-mini", + handoffs=["escalation_bot"], + tools=["search"], + output_type="str", ): pass spans = exporter.get_finished_spans() - create_span = next( + invoke_span = next( span for span in spans if span.attributes[GenAI.GEN_AI_OPERATION_NAME] - == GenAI.GenAiOperationNameValues.CREATE_AGENT.value + == GenAI.GenAiOperationNameValues.INVOKE_AGENT.value ) - assert create_span.kind is SpanKind.CLIENT - assert create_span.name == "create_agent support_bot" - assert create_span.attributes[GEN_AI_PROVIDER_NAME] == "openai" - assert create_span.attributes[GenAI.GEN_AI_AGENT_NAME] == "support_bot" - assert ( - create_span.attributes[GenAI.GEN_AI_AGENT_DESCRIPTION] - == "Answers support questions" - ) - assert create_span.attributes[GenAI.GEN_AI_AGENT_ID] == "agt_123" - assert ( - create_span.attributes[GenAI.GEN_AI_REQUEST_MODEL] == "gpt-4o-mini" - ) + assert invoke_span.kind is SpanKind.CLIENT + assert invoke_span.name == "invoke_agent support_bot" + assert invoke_span.attributes[GEN_AI_PROVIDER_NAME] == "openai" + assert invoke_span.attributes[GenAI.GEN_AI_AGENT_NAME] == "support_bot" finally: instrumentor.uninstrument() exporter.clear() @@ -425,7 +420,7 @@ def test_agent_name_override_applied_to_agent_spans(): try: with trace("workflow"): - with agent_span(operation="invoke", name="support_bot"): + with agent_span(name="support_bot"): pass spans = exporter.get_finished_spans() @@ -487,8 +482,26 @@ def __init__(self, input_tokens: int, output_tokens: int) -> None: class _Response: def __init__(self) -> None: self.id = "resp-123" + self.instructions = "You are a helpful assistant." self.model = "gpt-4o-mini" self.usage = _Usage(42, 9) + self.tools = [ + FunctionTool( + name="get_current_weather", + type="function", + description="Get the current weather in a given location", + parameters={ + "type": "object", + "properties": { + "location": { + "title": "Location", + "type": "string", + }, + }, + "required": ["location"], + }, + ) + ] self.output = [{"finish_reason": "stop"}] try: @@ -516,6 +529,30 @@ def __init__(self) -> None: assert response.attributes[GenAI.GEN_AI_RESPONSE_FINISH_REASONS] == ( "stop", ) + + system_instructions = json.loads( + response.attributes[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS] + ) + assert system_instructions == [ + {"type": "text", "content": "You are a helpful assistant."} + ] + tool_definitions = json.loads( + response.attributes[GEN_AI_TOOL_DEFINITIONS] + ) + assert tool_definitions == [ + { + "type": "function", + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": {"title": "Location", "type": "string"}, + }, + "required": ["location"], + }, + } + ] finally: instrumentor.uninstrument() exporter.clear() diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_z_span_processor_unit.py b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_z_span_processor_unit.py index b2c8c7c8f3..c879aa06a1 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_z_span_processor_unit.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_z_span_processor_unit.py @@ -156,19 +156,14 @@ def test_operation_and_span_naming(processor_setup): == sp.GenAIOperationName.EMBEDDINGS ) - agent_create = AgentSpanData(operation=" CREATE ") + # AgentSpanData always maps to invoke_agent (no operation field in OpenAI Agents SDK) + agent_data = AgentSpanData(name="bot") assert ( - processor._get_operation_name(agent_create) - == sp.GenAIOperationName.CREATE_AGENT - ) - - agent_invoke = AgentSpanData(operation="invoke_agent") - assert ( - processor._get_operation_name(agent_invoke) + processor._get_operation_name(agent_data) == sp.GenAIOperationName.INVOKE_AGENT ) - agent_default = AgentSpanData(operation=None) + agent_default = AgentSpanData() assert ( processor._get_operation_name(agent_default) == sp.GenAIOperationName.INVOKE_AGENT @@ -315,26 +310,19 @@ def __init__(self) -> None: agent_span = AgentSpanData( name="helper", output_type="json", - description="desc", - agent_id="agent-123", - model="model-x", - operation="invoke_agent", ) agent_attrs = _collect( processor._get_attributes_from_agent_span_data(agent_span, None) ) assert agent_attrs[sp.GEN_AI_AGENT_NAME] == "helper" - assert agent_attrs[sp.GEN_AI_AGENT_ID] == "agent-123" - assert agent_attrs[sp.GEN_AI_REQUEST_MODEL] == "model-x" + assert sp.GEN_AI_AGENT_ID not in agent_attrs + assert sp.GEN_AI_REQUEST_MODEL not in agent_attrs assert agent_attrs[sp.GEN_AI_OUTPUT_TYPE] == sp.GenAIOutputType.TEXT # Fallback to aggregated model when span data lacks it agent_span_no_model = AgentSpanData( name="helper-2", output_type="json", - description="desc", - agent_id="agent-456", - operation="invoke_agent", ) agent_content = { "input_messages": [], @@ -435,9 +423,7 @@ def test_span_lifecycle_and_shutdown(processor_setup): parent_span = FakeSpan( trace_id="trace-1", span_id="span-1", - span_data=AgentSpanData( - operation="invoke", name="agent", model="gpt-4o" - ), + span_data=AgentSpanData(name="agent"), started_at="2024-01-01T00:00:00Z", ended_at="2024-01-01T00:00:02Z", ) @@ -476,7 +462,7 @@ def test_span_lifecycle_and_shutdown(processor_setup): linger_span = FakeSpan( trace_id="trace-2", span_id="span-3", - span_data=AgentSpanData(operation=None), + span_data=AgentSpanData(), started_at="2024-01-01T00:00:06Z", ) processor.on_span_start(linger_span) @@ -518,7 +504,6 @@ def test_chat_span_renamed_with_model(processor_setup): trace_id=trace.trace_id, span_id="agent-span", span_data=AgentSpanData( - operation="invoke_agent", name="Agent", ), started_at="2025-01-01T00:00:00Z", diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md index 9132163d9b..6fc545df24 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md @@ -7,12 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Fix `ChoiceBuffer` crash on streaming tool-call deltas with `arguments=None` + ([#4350](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4350)) - Fix `StreamWrapper` missing `.headers` and other attributes when using `with_raw_response` streaming ([#4113](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/4113)) - Add opt-in support for latest experimental semantic conventions (v1.37.0). Set `OTEL_SEMCONV_STABILITY_OPT_IN` to `gen_ai_latest_experimental` to enable. Add dependency on `opentelemetry-util-genai` pypi package. ([#3715](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3715)) +- Add wrappers for OpenAI Responses API streams and response stream managers + ([#4280](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4280)) +- Add async wrappers for OpenAI Responses API streams and response stream managers + ([#4325](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4325)) +- Add strongly typed Responses API extractors with validation and content + extraction improvements + ([#4337](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4337)) ## Version 2.3b0 (2025-12-24) diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml index fc5939985b..73bf8d3c57 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Official OpenAI instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,16 +25,14 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-api ~= 1.37", - "opentelemetry-instrumentation ~= 0.58b0", - "opentelemetry-semantic-conventions ~= 0.58b0", - "opentelemetry-util-genai", + "opentelemetry-api ~= 1.39", + "opentelemetry-instrumentation ~= 0.60b0", + "opentelemetry-semantic-conventions ~= 0.60b0", + "opentelemetry-util-genai >= 0.4b0.dev", ] [project.optional-dependencies] -instruments = [ - "openai >= 1.26.0", -] +instruments = ["openai >= 1.26.0"] [project.entry-points.opentelemetry_instrumentor] openai = "opentelemetry.instrumentation.openai_v2:OpenAIInstrumentor" @@ -48,10 +45,7 @@ Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib" path = "src/opentelemetry/instrumentation/openai_v2/version.py" [tool.hatch.build.targets.sdist] -include = [ - "/src", - "/tests", -] +include = ["/src", "/tests"] [tool.hatch.build.targets.wheel] packages = ["src/opentelemetry"] diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py index 3bc42f103c..30f74cfab1 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py @@ -36,10 +36,10 @@ from opentelemetry.util.genai.types import ( ContentCapturingMode, Error, - LLMInvocation, + LLMInvocation, # pylint: disable=no-name-in-module # TODO: migrate to InferenceInvocation OutputMessage, Text, - ToolCall, + ToolCallRequest, ) from .instruments import Instruments @@ -582,7 +582,8 @@ def __init__(self, index, tool_call_id, function_name): self.arguments = [] def append_arguments(self, arguments): - self.arguments.append(arguments) + if arguments is not None: + self.arguments.append(arguments) class ChoiceBuffer: @@ -601,13 +602,16 @@ def append_tool_call(self, tool_call): for _ in range(len(self.tool_calls_buffers), idx + 1): self.tool_calls_buffers.append(None) + function = tool_call.function if not self.tool_calls_buffers[idx]: self.tool_calls_buffers[idx] = ToolCallBuffer( - idx, tool_call.id, tool_call.function.name + idx, + tool_call.id, + function.name if function else None, ) - self.tool_calls_buffers[idx].append_arguments( - tool_call.function.arguments - ) + + if function: + self.tool_calls_buffers[idx].append_arguments(function.arguments) class BaseStreamWrapper: @@ -910,7 +914,7 @@ def _set_output_messages(self): arguments = json.loads(arguments_str) except json.JSONDecodeError: arguments = arguments_str - tool_call_part = ToolCall( + tool_call_part = ToolCallRequest( name=tool_call.function_name, id=tool_call.tool_call_id, arguments=arguments, diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/response_extractors.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/response_extractors.py new file mode 100644 index 0000000000..2d5e8702b1 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/response_extractors.py @@ -0,0 +1,419 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import logging +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, List, Optional, TypeVar, Union + +from pydantic import BaseModel, Field, StrictInt, StrictStr, ValidationError + +from opentelemetry.semconv._incubating.attributes import ( + openai_attributes as OpenAIAttributes, +) + +_PYDANTIC_V2 = hasattr(BaseModel, "model_validate") + +if _PYDANTIC_V2: + from pydantic import ConfigDict +else: + ConfigDict = None + +if TYPE_CHECKING: + from opentelemetry.util.genai.types import ( + InputMessage, + LLMInvocation, + OutputMessage, + Text, + ) + +try: + from opentelemetry.util.genai.types import ( + InputMessage, + OutputMessage, + Text, + ) +except ImportError: + InputMessage = None + OutputMessage = None + Text = None + +GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens" +GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = ( + "gen_ai.usage.cache_creation.input_tokens" +) + +_logger = logging.getLogger(__name__) +ModelT = TypeVar("ModelT", bound=BaseModel) + + +class _ExtractorModel(BaseModel): + if _PYDANTIC_V2: + model_config = ConfigDict(extra="ignore", from_attributes=True) + else: + + class Config: + extra = "ignore" + orm_mode = True + + +class _ResponseTextFormatModel(_ExtractorModel): + type: Optional[StrictStr] = None + + +class _ResponseTextConfigModel(_ExtractorModel): + format: Optional[_ResponseTextFormatModel] = None + + +class _ResponseInputContentModel(_ExtractorModel): + text: Optional[StrictStr] = None + + +class _ResponseInputItemModel(_ExtractorModel): + role: Optional[StrictStr] = None + content: Optional[Union[StrictStr, List[_ResponseInputContentModel]]] = ( + None + ) + + +class _ResponsesRequestModel(_ExtractorModel): + instructions: Optional[StrictStr] = None + input: Optional[Union[StrictStr, List[_ResponseInputItemModel]]] = None + text: Optional[_ResponseTextConfigModel] = None + + +class _ResponseOutputContentModel(_ExtractorModel): + type: Optional[StrictStr] = None + text: Optional[StrictStr] = None + refusal: Optional[StrictStr] = None + + +class _ResponseOutputItemModel(_ExtractorModel): + type: Optional[StrictStr] = None + role: Optional[StrictStr] = None + status: Optional[StrictStr] = None + content: List[_ResponseOutputContentModel] = Field(default_factory=list) + + +class _UsageDetailsModel(_ExtractorModel): + cached_tokens: Optional[StrictInt] = None + cache_creation_input_tokens: Optional[StrictInt] = None + + +class _UsageModel(_ExtractorModel): + input_tokens: Optional[StrictInt] = None + output_tokens: Optional[StrictInt] = None + prompt_tokens: Optional[StrictInt] = None + completion_tokens: Optional[StrictInt] = None + input_tokens_details: Optional[_UsageDetailsModel] = None + prompt_tokens_details: Optional[_UsageDetailsModel] = None + + +class _ResponsesResultModel(_ExtractorModel): + # Responses can be partial or otherwise omit `output`. Treat that as "no + # terminal message items available yet" so downstream extraction helpers + # naturally return empty lists instead of raising. + output: List[_ResponseOutputItemModel] = Field(default_factory=list) + model: Optional[StrictStr] = None + id: Optional[StrictStr] = None + service_tier: Optional[StrictStr] = None + usage: Optional[_UsageModel] = None + + +def _rebuild_model(model_type: type[BaseModel]) -> None: + if _PYDANTIC_V2: + model_type.model_rebuild(_types_namespace=globals()) + else: + model_type.update_forward_refs(**globals()) + + +for _model_type in ( + _ResponseTextFormatModel, + _ResponseTextConfigModel, + _ResponseInputContentModel, + _ResponseInputItemModel, + _ResponsesRequestModel, + _ResponseOutputContentModel, + _ResponseOutputItemModel, + _UsageDetailsModel, + _UsageModel, + _ResponsesResultModel, +): + _rebuild_model(_model_type) + + +def _validate_model( + model_type: type[ModelT], value: object, context: str +) -> ModelT | None: + try: + if _PYDANTIC_V2: + return model_type.model_validate(value) + if isinstance(value, Mapping): + return model_type.parse_obj(value) + return model_type.from_orm(value) + except ValidationError: + _logger.debug( + "OpenAI responses extractor validation failed for %s", + context, + exc_info=True, + ) + return None + + +def _validate_request_kwargs( + kwargs: Mapping[str, object], +) -> _ResponsesRequestModel | None: + return _validate_model( + _ResponsesRequestModel, kwargs, "request parameters" + ) + + +def _validate_response_result(result: object) -> _ResponsesResultModel | None: + return _validate_model(_ResponsesResultModel, result, "response payload") + + +def _coerce_response_result( + result: object | _ResponsesResultModel | None, +) -> _ResponsesResultModel | None: + if result is None: + return None + + if isinstance(result, _ResponsesResultModel): + return result + + return _validate_response_result(result) + + +def _extract_system_instruction(kwargs: Mapping[str, object]) -> list["Text"]: + """Extract system instruction from the ``instructions`` parameter.""" + if Text is None: + return [] + + request = _validate_request_kwargs(kwargs) + if request is None or request.instructions is None: + return [] + + return [Text(content=request.instructions)] + + +def _extract_input_messages( + kwargs: Mapping[str, object], +) -> list["InputMessage"]: + """Extract input messages from Responses API kwargs.""" + if InputMessage is None or Text is None: + return [] + + request = _validate_request_kwargs(kwargs) + if request is None or request.input is None: + return [] + + if isinstance(request.input, str): + return [InputMessage(role="user", parts=[Text(content=request.input)])] + + messages: list[InputMessage] = [] + for item in request.input: + if item.role is None: + continue + + if isinstance(item.content, str): + messages.append( + InputMessage( + role=item.role, parts=[Text(content=item.content)] + ) + ) + continue + + if item.content is None: + continue + + parts = [ + Text(content=part.text) + for part in item.content + if part.text is not None + ] + if parts: + messages.append(InputMessage(role=item.role, parts=parts)) + + return messages + + +def _extract_output_parts( + content_blocks: Sequence[_ResponseOutputContentModel], +) -> list["Text"]: + if Text is None: + return [] + + parts: list[Text] = [] + for block in content_blocks: + if block.type == "output_text" and block.text is not None: + parts.append(Text(content=block.text)) + elif block.type == "refusal" and block.refusal is not None: + parts.append(Text(content=block.refusal)) + return parts + + +def _finish_reason_from_status(status: str | None) -> str | None: + # Responses API output items expose lifecycle statuses rather than finish + # reasons. We map the normal terminal state to the GenAI "stop" reason, + # preserve the other terminal statuses verbatim, and drop non-terminal or + # unknown states from the finish_reasons attribute. + if status == "completed": + return "stop" + if status in {"failed", "cancelled", "incomplete"}: + return status + return None + + +def _extract_output_messages_from_model( + result: _ResponsesResultModel, +) -> list["OutputMessage"]: + if OutputMessage is None or Text is None: + return [] + + messages: list[OutputMessage] = [] + for item in result.output: + if item.type != "message": + continue + finish_reason = _finish_reason_from_status(item.status) + if finish_reason is None: + continue + + messages.append( + OutputMessage( + role=item.role if item.role is not None else "assistant", + parts=_extract_output_parts(item.content), + finish_reason=finish_reason, + ) + ) + + return messages + + +def _extract_output_messages( + result: object | _ResponsesResultModel | None, +) -> list["OutputMessage"]: + """Extract output messages from a Responses API result.""" + validated_result = _coerce_response_result(result) + if validated_result is None: + return [] + + return _extract_output_messages_from_model(validated_result) + + +def _extract_finish_reasons_from_model( + result: _ResponsesResultModel, +) -> list[str]: + finish_reasons: list[str] = [] + for item in result.output: + if item.type != "message": + continue + finish_reason = _finish_reason_from_status(item.status) + if finish_reason is not None: + finish_reasons.append(finish_reason) + return finish_reasons + + +def _extract_finish_reasons( + result: object | _ResponsesResultModel | None, +) -> list[str]: + """Extract finish reasons from Responses API output items.""" + validated_result = _coerce_response_result(result) + if validated_result is None: + return [] + + return _extract_finish_reasons_from_model(validated_result) + + +def _extract_output_type(kwargs: Mapping[str, object]) -> str | None: + """Extract output type from Responses API request text.format.""" + request = _validate_request_kwargs(kwargs) + if request is None or request.text is None or request.text.format is None: + return None + + if request.text.format.type == "json_schema": + return "json" + return request.text.format.type + + +def _set_invocation_usage_attributes( + invocation: "LLMInvocation", usage: _UsageModel +) -> None: + if usage.input_tokens is not None: + invocation.input_tokens = usage.input_tokens + else: + invocation.input_tokens = usage.prompt_tokens + + if usage.output_tokens is not None: + invocation.output_tokens = usage.output_tokens + else: + invocation.output_tokens = usage.completion_tokens + + details = ( + usage.input_tokens_details + if usage.input_tokens_details is not None + else usage.prompt_tokens_details + ) + if details is None: + return + + if details.cached_tokens is not None: + invocation.attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = ( + details.cached_tokens + ) + + if details.cache_creation_input_tokens is not None: + invocation.attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS] = ( + details.cache_creation_input_tokens + ) + + +def _set_invocation_response_attributes( + invocation: "LLMInvocation", + result: object | None, + capture_content: bool, +) -> None: + # This helper sits at the raw response boundary for instrumentation. Unlike + # the extractor helpers above, it intentionally validates exactly once and + # then fans out to the `_from_model` helpers rather than accepting an + # already-coerced `_ResponsesResultModel`. + if result is None: + return + + validated_result = _validate_response_result(result) + if validated_result is None: + return + + if validated_result.model is not None: + invocation.response_model_name = validated_result.model + + if validated_result.id is not None: + invocation.response_id = validated_result.id + + if validated_result.service_tier is not None: + invocation.attributes[ + OpenAIAttributes.OPENAI_RESPONSE_SERVICE_TIER + ] = validated_result.service_tier + + if validated_result.usage is not None: + _set_invocation_usage_attributes(invocation, validated_result.usage) + + finish_reasons = _extract_finish_reasons_from_model(validated_result) + if finish_reasons: + invocation.finish_reasons = finish_reasons + + if capture_content: + output_messages = _extract_output_messages_from_model(validated_result) + if output_messages: + invocation.output_messages = output_messages diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/response_wrappers.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/response_wrappers.py new file mode 100644 index 0000000000..402e5c84c2 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/response_wrappers.py @@ -0,0 +1,467 @@ +"""Wrappers for OpenAI Responses API streams and stream managers.""" + +from __future__ import annotations + +import logging +from contextlib import AsyncExitStack, ExitStack, contextmanager +from types import TracebackType +from typing import TYPE_CHECKING, Callable, Generator, Generic, TypeVar + +from opentelemetry.util.genai.handler import TelemetryHandler +from opentelemetry.util.genai.types import ( + Error, + LLMInvocation, # pylint: disable=no-name-in-module # TODO: migrate to InferenceInvocation +) + +# OpenAI Responses internals are version-gated (added in openai>=1.66.0), so +# pylint may not resolve them in all lint environments even though we guard +# runtime usage with ImportError fallbacks below. +try: + from openai.lib.streaming.responses._events import ( # pylint: disable=no-name-in-module + ResponseCompletedEvent, + ) + from openai.types.responses import ( # pylint: disable=no-name-in-module + ResponseCreatedEvent, + ResponseErrorEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseInProgressEvent, + ) + + _RESPONSE_EVENTS_WITH_RESPONSE = ( + ResponseCreatedEvent, + ResponseInProgressEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseCompletedEvent, + ) +except ImportError: + ResponseCompletedEvent = None + ResponseCreatedEvent = None + ResponseErrorEvent = None + ResponseFailedEvent = None + ResponseIncompleteEvent = None + ResponseInProgressEvent = None + _RESPONSE_EVENTS_WITH_RESPONSE = () + +try: + from opentelemetry.instrumentation.openai_v2.response_extractors import ( # pylint: disable=no-name-in-module + _set_invocation_response_attributes, + ) +except ImportError: + _set_invocation_response_attributes = None + +if TYPE_CHECKING: + from openai.lib.streaming.responses._events import ( # pylint: disable=no-name-in-module + ResponseStreamEvent, + ) + from openai.lib.streaming.responses._responses import ( + AsyncResponseStream, + AsyncResponseStreamManager, + ResponseStream, + ResponseStreamManager, + ) # pylint: disable=no-name-in-module + from openai.types.responses import ( # pylint: disable=no-name-in-module + ParsedResponse, + Response, + ) + +_logger = logging.getLogger(__name__) +TextFormatT = TypeVar("TextFormatT") +ResponseT = TypeVar("ResponseT") + + +def _set_response_attributes( + invocation: "LLMInvocation", + result: "ParsedResponse[TextFormatT] | Response | None", + capture_content: bool, +) -> None: + if _set_invocation_response_attributes is None: + return + _set_invocation_response_attributes(invocation, result, capture_content) + + +def _get_stream_response(stream): + try: + return stream._response + except AttributeError: + try: + return stream.response + except AttributeError: + return None + + +class _ResponseProxy(Generic[ResponseT]): + def __init__(self, response: ResponseT, finalize: Callable[[], None]): + self._response = response + self._finalize = finalize + + def close(self) -> None: + try: + self._response.close() + finally: + self._finalize() + + def __getattr__(self, name: str): + return getattr(self._response, name) + + +class _AsyncResponseProxy(Generic[ResponseT]): + def __init__(self, response: ResponseT, finalize: Callable[[], None]): + self._response = response + self._finalize = finalize + + async def aclose(self) -> None: + try: + await self._response.aclose() + finally: + self._finalize() + + def __getattr__(self, name: str): + return getattr(self._response, name) + + +class ResponseStreamWrapper(Generic[TextFormatT]): + """Wrapper for OpenAI Responses API stream objects. + + Wraps ResponseStream from the OpenAI SDK: + https://github.com/openai/openai-python/blob/656e3cab4a18262a49b961d41293367e45ee71b9/src/openai/_streaming.py#L55 + """ + + def __init__( + self, + stream: "ResponseStream[TextFormatT]", + handler: TelemetryHandler, + invocation: "LLMInvocation", + capture_content: bool, + ): + self.stream = stream + self.handler = handler + self.invocation = invocation + self._capture_content = capture_content + self._finalized = False + + def __enter__(self) -> "ResponseStreamWrapper": + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + if exc_type is not None: + self._fail( + str(exc_val), type(exc_val) if exc_val else Exception + ) + finally: + self.close() + return False + + def close(self) -> None: + try: + self.stream.close() + finally: + self._stop(None) + + def __iter__(self) -> "ResponseStreamWrapper": + return self + + def __next__(self) -> "ResponseStreamEvent[TextFormatT]": + try: + event = next(self.stream) + except StopIteration: + self._stop(None) + raise + except Exception as error: + self._fail(str(error), type(error)) + raise + with self._safe_instrumentation("event processing"): + self.process_event(event) + return event + + def get_final_response(self) -> "ParsedResponse[TextFormatT]": + self.until_done() + return self.stream.get_final_response() + + def until_done(self) -> "ResponseStreamWrapper": + for _ in self: + pass + return self + + def parse(self) -> "ResponseStreamWrapper": + raise NotImplementedError( + "ResponseStreamWrapper.parse() is not implemented" + ) + + # TODO: Replace __getattr__ passthrough with wrapt.ObjectProxy in a future + # cleanup once wrapt 2 typing support is available (wrapt PR #3903). + def __getattr__(self, name: str): + return getattr(self.stream, name) + + @property + def response(self): + response = _get_stream_response(self.stream) + if response is None: + return None + return _ResponseProxy(response, lambda: self._stop(None)) + + def _stop( + self, result: "ParsedResponse[TextFormatT] | Response | None" + ) -> None: + if self._finalized: + return + with self._safe_instrumentation("response attribute extraction"): + _set_response_attributes( + self.invocation, result, self._capture_content + ) + with self._safe_instrumentation("stop_llm"): + self.handler.stop_llm(self.invocation) + self._finalized = True + + def _fail(self, message: str, error_type: type[BaseException]) -> None: + if self._finalized: + return + with self._safe_instrumentation("fail_llm"): + self.handler.fail_llm( + self.invocation, Error(message=message, type=error_type) + ) + self._finalized = True + + @staticmethod + @contextmanager + def _safe_instrumentation(context: str) -> Generator[None, None, None]: + try: + yield + except Exception: # pylint: disable=broad-exception-caught + _logger.debug( + "OpenAI responses instrumentation error during %s", + context, + exc_info=True, + stacklevel=2, + ) + + def process_event(self, event: "ResponseStreamEvent[TextFormatT]") -> None: + event_type = event.type + response: "ParsedResponse[TextFormatT] | Response | None" = None + + if isinstance(event, _RESPONSE_EVENTS_WITH_RESPONSE): + response = event.response + + if response and not self.invocation.request_model: + model = response.model + if model: + self.invocation.request_model = model + + if isinstance(event, ResponseCompletedEvent): + self._stop(response) + return + + if isinstance(event, (ResponseFailedEvent, ResponseIncompleteEvent)): + with self._safe_instrumentation("response attribute extraction"): + _set_response_attributes( + self.invocation, response, self._capture_content + ) + self._fail(event_type, RuntimeError) + return + + if isinstance(event, ResponseErrorEvent): + error_type = event.code or "response.error" + message = event.message or error_type + self._fail(message, RuntimeError) + + +class ResponseStreamManagerWrapper(Generic[TextFormatT]): + """Wrapper for OpenAI Responses API stream managers. + + Wraps ResponseStreamManager from the OpenAI SDK: + https://github.com/openai/openai-python/blob/656e3cab4a18262a49b961d41293367e45ee71b9/src/openai/lib/streaming/responses/_responses.py#L95 + """ + + def __init__( + self, + manager: "ResponseStreamManager[TextFormatT]", + handler: TelemetryHandler, + invocation: "LLMInvocation", + capture_content: bool, + ): + self._manager = manager + self._handler = handler + self._invocation = invocation + self._capture_content = capture_content + self._stream_wrapper: ResponseStreamWrapper[TextFormatT] | None = None + + def __enter__(self) -> ResponseStreamWrapper[TextFormatT]: + stream = self._manager.__enter__() + self._stream_wrapper = ResponseStreamWrapper( + stream, + self._handler, + self._invocation, + self._capture_content, + ) + return self._stream_wrapper + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + suppressed = False + stream_wrapper = self._stream_wrapper + self._stream_wrapper = None + with ExitStack() as cleanup: + if stream_wrapper is not None: + + def finalize_stream_wrapper() -> None: + if suppressed: + stream_wrapper.__exit__(None, None, None) + else: + stream_wrapper.__exit__(exc_type, exc_val, exc_tb) + + cleanup.callback(finalize_stream_wrapper) + suppressed = self._manager.__exit__(exc_type, exc_val, exc_tb) + return suppressed + + def parse(self) -> "ResponseStreamManagerWrapper[TextFormatT]": + raise NotImplementedError( + "ResponseStreamManagerWrapper.parse() is not implemented" + ) + + # TODO: Replace __getattr__ passthrough with wrapt.ObjectProxy in a future + # cleanup once wrapt 2 typing support is available (wrapt PR #3903). + def __getattr__(self, name: str): + return getattr(self._manager, name) + + +class AsyncResponseStreamWrapper(ResponseStreamWrapper[TextFormatT]): + """Wrapper for async OpenAI Responses API stream objects.""" + + stream: "AsyncResponseStream[TextFormatT]" + + async def __aenter__(self) -> "AsyncResponseStreamWrapper[TextFormatT]": + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + if exc_type is not None: + self._fail( + str(exc_val), type(exc_val) if exc_val else Exception + ) + finally: + await self.close() + return False + + async def close(self) -> None: + try: + await self.stream.close() + finally: + self._stop(None) + + def __aiter__(self) -> "AsyncResponseStreamWrapper[TextFormatT]": + return self + + async def __anext__(self) -> "ResponseStreamEvent[TextFormatT]": + try: + event = await self.stream.__anext__() + except StopAsyncIteration: + self._stop(None) + raise + except Exception as error: + self._fail(str(error), type(error)) + raise + with self._safe_instrumentation("event processing"): + self.process_event(event) + return event + + async def get_final_response(self) -> "ParsedResponse[TextFormatT]": + await self.until_done() + return await self.stream.get_final_response() + + async def until_done(self) -> "AsyncResponseStreamWrapper[TextFormatT]": + async for _ in self: + pass + return self + + def parse(self) -> "AsyncResponseStreamWrapper[TextFormatT]": + raise NotImplementedError( + "AsyncResponseStreamWrapper.parse() is not implemented" + ) + + @property + def response(self): + response = _get_stream_response(self.stream) + if response is None: + return None + return _AsyncResponseProxy(response, lambda: self._stop(None)) + + +class AsyncResponseStreamManagerWrapper(Generic[TextFormatT]): + """Wrapper for async OpenAI Responses API stream managers.""" + + def __init__( + self, + manager: "AsyncResponseStreamManager[TextFormatT]", + handler: TelemetryHandler, + invocation: "LLMInvocation", + capture_content: bool, + ): + self._manager = manager + self._handler = handler + self._invocation = invocation + self._capture_content = capture_content + self._stream_wrapper: ( + AsyncResponseStreamWrapper[TextFormatT] | None + ) = None + + async def __aenter__(self) -> AsyncResponseStreamWrapper[TextFormatT]: + stream = await self._manager.__aenter__() + self._stream_wrapper = AsyncResponseStreamWrapper( + stream, + self._handler, + self._invocation, + self._capture_content, + ) + return self._stream_wrapper + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + suppressed = False + stream_wrapper = self._stream_wrapper + self._stream_wrapper = None + async with AsyncExitStack() as cleanup: + if stream_wrapper is not None: + + async def finalize_stream_wrapper() -> None: + if suppressed: + await stream_wrapper.__aexit__(None, None, None) + else: + await stream_wrapper.__aexit__( + exc_type, exc_val, exc_tb + ) + + cleanup.push_async_callback(finalize_stream_wrapper) + suppressed = await self._manager.__aexit__( + exc_type, exc_val, exc_tb + ) + return suppressed + + def parse(self) -> "AsyncResponseStreamManagerWrapper[TextFormatT]": + raise NotImplementedError( + "AsyncResponseStreamManagerWrapper.parse() is not implemented" + ) + + # TODO: Replace __getattr__ passthrough with wrapt.ObjectProxy in a future + # cleanup once wrapt 2 typing support is available (wrapt PR #3903). + def __getattr__(self, name: str): + return getattr(self._manager, name) diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py index 7e5d2307ca..bd566ddba5 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py @@ -38,10 +38,10 @@ from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util.genai.types import ( InputMessage, - LLMInvocation, + LLMInvocation, # pylint: disable=no-name-in-module # TODO: migrate to InferenceInvocation OutputMessage, Text, - ToolCall, + ToolCallRequest, ToolCallResponse, ) @@ -452,7 +452,7 @@ def _prepare_input_messages(messages) -> List[InputMessage]: return chat_messages -def extract_tool_calls_new(tool_calls) -> list[ToolCall]: +def extract_tool_calls_new(tool_calls) -> list[ToolCallRequest]: parts = [] for tool_call in tool_calls: call_id = get_property_value(tool_call, "id") @@ -470,7 +470,9 @@ def extract_tool_calls_new(tool_calls) -> list[ToolCall]: arguments = arguments_str # TODO: support custom - parts.append(ToolCall(id=call_id, name=func_name, arguments=arguments)) + parts.append( + ToolCallRequest(id=call_id, name=func_name, arguments=arguments) + ) return parts diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt index 2644ba47e8..45339fb438 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt @@ -29,9 +29,9 @@ pytest-vcr==1.0.2 pytest-asyncio==0.21.0 wrapt==1.16.0 opentelemetry-exporter-otlp-proto-http~=1.30 -opentelemetry-api==1.37 # when updating, also update in pyproject.toml -opentelemetry-sdk==1.37 # when updating, also update in pyproject.toml -opentelemetry-semantic-conventions==0.58b0 # when updating, also update in pyproject.toml +opentelemetry-api==1.39 # when updating, also update in pyproject.toml +opentelemetry-sdk==1.39 # when updating, also update in pyproject.toml +opentelemetry-semantic-conventions==0.60b0 # when updating, also update in pyproject.toml -e instrumentation-genai/opentelemetry-instrumentation-openai-v2 -e util/opentelemetry-util-genai \ No newline at end of file diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.pydantic1.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.pydantic1.txt new file mode 100644 index 0000000000..9ca166184d --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.pydantic1.txt @@ -0,0 +1,55 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# ******************************** +# WARNING: NOT HERMETIC !!!!!!!!!! +# ******************************** +# +# This "requirements.txt" is installed in conjunction +# with multiple other dependencies in the top-level "tox.ini" +# file. In particular, please see: +# +# openai-pydantic1: {[testenv]test_deps} +# openai-pydantic1: -r {toxinidir}/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.pydantic1.txt +# +# This provides additional dependencies, namely: +# +# opentelemetry-api +# opentelemetry-sdk +# opentelemetry-semantic-conventions +# +# ... with a "dev" version based on the latest distribution. + + +# This variant of the requirements aims to test the system using +# the newest supported version of external dependencies with Pydantic 1.x. + +openai==1.109.1 +pydantic==1.10.24 +httpx==0.27.2 +# older jiter is required for PyPy < 3.11 +jiter==0.11.1 +Deprecated==1.2.14 +importlib-metadata==6.11.0 +packaging==24.0 +pytest==7.4.4 +pytest-vcr==1.0.2 +pytest-asyncio==0.21.0 +wrapt==1.16.0 +# test with the latest version of opentelemetry-api, sdk, and semantic conventions + +-e opentelemetry-instrumentation +-e instrumentation-genai/opentelemetry-instrumentation-openai-v2 +-e util/opentelemetry-util-genai diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py index 5da88e6b5a..c19a8b3d5b 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py @@ -418,6 +418,9 @@ async def test_chat_completion_with_raw_response_streaming( response = raw_response.parse() message_content = "" + response_stream_usage = None + response_stream_model = None + response_stream_id = None async for chunk in response: if chunk.choices: message_content += chunk.choices[0].delta.content or "" @@ -983,6 +986,9 @@ async def test_async_chat_completion_multiple_choices_streaming( stream_options={"include_usage": True}, ) + response_stream_usage = None + response_stream_model = None + response_stream_id = None # two strings for each choice response_stream_result = ["", ""] finish_reasons = ["", ""] @@ -1203,6 +1209,9 @@ async def async_chat_completion_multiple_tools_streaming( ) finish_reason = None + response_stream_usage = None + response_stream_model = None + response_stream_id = None # two tools tool_names = ["", ""] tool_call_ids = ["", ""] diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py index 4862460ddf..3e4df914dc 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py @@ -561,6 +561,9 @@ def test_chat_completion_with_raw_response_streaming( response = raw_response.parse() message_content = "" + response_stream_usage = None + response_stream_model = None + response_stream_id = None for chunk in response: if chunk.choices: message_content += chunk.choices[0].delta.content or "" @@ -1083,6 +1086,9 @@ def test_chat_completion_multiple_choices_streaming( # two strings for each choice response_stream_result = ["", ""] finish_reasons = ["", ""] + response_stream_usage = None + response_stream_model = None + response_stream_id = None for chunk in response_0: if chunk.choices: for choice in chunk.choices: @@ -1274,6 +1280,9 @@ def test_chat_completion_with_context_manager_streaming( stream_options={"include_usage": True}, ) as response: message_content = "" + response_stream_usage = None + response_stream_model = None + response_stream_id = None for chunk in response: if chunk.choices: message_content += chunk.choices[0].delta.content or "" @@ -1345,6 +1354,9 @@ def chat_completion_multiple_tools_streaming( ) finish_reason = None + response_stream_usage = None + response_stream_model = None + response_stream_id = None # two tools tool_names = ["", ""] tool_call_ids = ["", ""] diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_choice_buffer.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_choice_buffer.py new file mode 100644 index 0000000000..7717ff73b2 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_choice_buffer.py @@ -0,0 +1,182 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for ChoiceBuffer and ToolCallBuffer classes.""" + +from openai.types.chat.chat_completion_chunk import ( + ChoiceDeltaToolCall, + ChoiceDeltaToolCallFunction, +) + +from opentelemetry.instrumentation.openai_v2.patch import ( + ChoiceBuffer, + ToolCallBuffer, +) + + +def test_toolcallbuffer_append_arguments_with_string(): + buf = ToolCallBuffer(0, "call_1", "get_weather") + buf.append_arguments('{"city":') + buf.append_arguments(' "NYC"}') + assert "".join(buf.arguments) == '{"city": "NYC"}' + + +def test_toolcallbuffer_append_arguments_with_none_is_skipped(): + """Regression test for issue #4344. + + Some OpenAI-compatible providers (vLLM, TGI, etc.) send + arguments=None on tool-call delta chunks instead of arguments="". + This must not crash when joining the arguments list. + """ + buf = ToolCallBuffer(0, "call_1", "get_weather") + buf.append_arguments(None) + buf.append_arguments('{"city": "NYC"}') + buf.append_arguments(None) + assert "".join(buf.arguments) == '{"city": "NYC"}' + + +def test_toolcallbuffer_append_arguments_all_none(): + buf = ToolCallBuffer(0, "call_1", "get_weather") + buf.append_arguments(None) + buf.append_arguments(None) + assert "".join(buf.arguments) == "" + + +def test_toolcallbuffer_append_arguments_empty_string(): + buf = ToolCallBuffer(0, "call_1", "get_weather") + buf.append_arguments("") + buf.append_arguments('{"city": "NYC"}') + assert "".join(buf.arguments) == '{"city": "NYC"}' + + +def test_choicebuffer_append_tool_call_with_none_arguments(): + """End-to-end regression test for issue #4344. + + Simulates the exact scenario from the bug report where a provider + sends arguments=None on the first tool-call delta chunk. + """ + buf = ChoiceBuffer(0) + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + id="call_1", + type="function", + function=ChoiceDeltaToolCallFunction( + name="get_weather", arguments=None + ), + ) + ) + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + function=ChoiceDeltaToolCallFunction(arguments='{"city": "NYC"}'), + ) + ) + + # This must not raise TypeError + result = "".join(buf.tool_calls_buffers[0].arguments) + assert result == '{"city": "NYC"}' + + +def test_choicebuffer_append_tool_call_normal_flow(): + """Standard OpenAI flow where arguments="" on first delta.""" + buf = ChoiceBuffer(0) + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + id="call_1", + type="function", + function=ChoiceDeltaToolCallFunction( + name="get_weather", arguments="" + ), + ) + ) + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + function=ChoiceDeltaToolCallFunction(arguments='{"city": "NYC"}'), + ) + ) + + result = "".join(buf.tool_calls_buffers[0].arguments) + assert result == '{"city": "NYC"}' + + +def test_choicebuffer_append_multiple_tool_calls_with_none_arguments(): + """Multiple tool calls where some have arguments=None.""" + buf = ChoiceBuffer(0) + + # First tool call + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + id="call_1", + type="function", + function=ChoiceDeltaToolCallFunction( + name="get_weather", arguments=None + ), + ) + ) + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + function=ChoiceDeltaToolCallFunction(arguments='{"city": "NYC"}'), + ) + ) + + # Second tool call + buf.append_tool_call( + ChoiceDeltaToolCall( + index=1, + id="call_2", + type="function", + function=ChoiceDeltaToolCallFunction( + name="get_time", arguments=None + ), + ) + ) + buf.append_tool_call( + ChoiceDeltaToolCall( + index=1, + function=ChoiceDeltaToolCallFunction(arguments='{"tz": "EST"}'), + ) + ) + + assert "".join(buf.tool_calls_buffers[0].arguments) == '{"city": "NYC"}' + assert "".join(buf.tool_calls_buffers[1].arguments) == '{"tz": "EST"}' + + +def test_choicebuffer_append_tool_call_with_none_function(): + """Handle delta chunks where function is None.""" + buf = ChoiceBuffer(0) + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + id="call_1", + type="function", + function=ChoiceDeltaToolCallFunction( + name="get_weather", arguments='{"city": "NYC"}' + ), + ) + ) + # Subsequent delta with function=None should not crash + buf.append_tool_call( + ChoiceDeltaToolCall( + index=0, + function=None, + ) + ) + + result = "".join(buf.tool_calls_buffers[0].arguments) + assert result == '{"city": "NYC"}' diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_response_extractors.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_response_extractors.py new file mode 100644 index 0000000000..1cf9a1a70e --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_response_extractors.py @@ -0,0 +1,365 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from types import SimpleNamespace +from unittest import mock + +import pytest + +from opentelemetry.instrumentation.openai_v2 import response_extractors +from opentelemetry.semconv._incubating.attributes import ( + openai_attributes as OpenAIAttributes, +) +from opentelemetry.util.genai.types import LLMInvocation + + +def _validate_compat_model(loaded_module, model_type, value): + return loaded_module._validate_model(model_type, value, "test") + + +@pytest.fixture(scope="module", name="loaded_module") +def _loaded_module_fixture(): + return response_extractors + + +@pytest.fixture( + scope="module", name="gen_ai_usage_cache_creation_input_tokens" +) +def _gen_ai_usage_cache_creation_input_tokens_fixture(loaded_module): + return loaded_module.GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS + + +@pytest.fixture(scope="module", name="gen_ai_usage_cache_read_input_tokens") +def _gen_ai_usage_cache_read_input_tokens_fixture(loaded_module): + return loaded_module.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS + + +def test_extract_system_instruction_returns_text_for_string(loaded_module): + instructions = loaded_module._extract_system_instruction( + {"instructions": "Be concise"} + ) + + assert [part.content for part in instructions] == ["Be concise"] + + +def test_extract_input_messages_supports_string_and_mixed_message_content( + loaded_module, +): + from_string = loaded_module._extract_input_messages({"input": "Hello"}) + from_list = loaded_module._extract_input_messages( + { + "input": [ + {"role": "user", "content": "First"}, + SimpleNamespace( + role="assistant", + content=[ + {"text": "Second"}, + SimpleNamespace(text="Third"), + {"type": "input_image", "image_url": "ignored"}, + ], + ), + {"role": None, "content": "ignored"}, + ] + } + ) + + assert [ + (msg.role, [part.content for part in msg.parts]) for msg in from_string + ] == [("user", ["Hello"])] + assert [ + (msg.role, [part.content for part in msg.parts]) for msg in from_list + ] == [ + ("user", ["First"]), + ("assistant", ["Second", "Third"]), + ] + + +def test_extract_output_messages_maps_parts_and_finish_reasons(loaded_module): + result = SimpleNamespace( + output=[ + SimpleNamespace( + type="message", + role="assistant", + status="completed", + content=[ + SimpleNamespace(type="output_text", text="Done"), + SimpleNamespace(type="refusal", refusal="Cannot comply"), + SimpleNamespace(type="summary", text="ignored"), + ], + ), + SimpleNamespace( + type="message", + status="incomplete", + content=[SimpleNamespace(type="output_text", text="Partial")], + ), + SimpleNamespace( + type="message", + status="queued", + content=[SimpleNamespace(type="output_text", text="Pending")], + ), + SimpleNamespace(type="tool_call", status="completed", content=[]), + ] + ) + + messages = loaded_module._extract_output_messages(result) + + assert [(msg.role, msg.finish_reason) for msg in messages] == [ + ("assistant", "stop"), + ("assistant", "incomplete"), + ] + assert [[part.content for part in msg.parts] for msg in messages] == [ + ["Done", "Cannot comply"], + ["Partial"], + ] + + +def test_extract_finish_reasons_only_reads_message_items(loaded_module): + result = SimpleNamespace( + output=[ + SimpleNamespace(type="message", status="completed"), + SimpleNamespace(type="message", status=None), + SimpleNamespace(type="message", status="in_progress"), + SimpleNamespace(type="tool_call", status="incomplete"), + ] + ) + + assert loaded_module._extract_finish_reasons(result) == ["stop"] + + +def test_extract_output_type_handles_text_format_mapping(loaded_module): + assert ( + loaded_module._extract_output_type( + {"text": {"format": {"type": "json_schema"}}} + ) + == "json" + ) + assert ( + loaded_module._extract_output_type( + {"text": {"format": {"type": "text"}}} + ) + == "text" + ) + assert ( + loaded_module._extract_output_type( + { + "text": SimpleNamespace( + format=SimpleNamespace(type="json_schema") + ) + } + ) + == "json" + ) + assert ( + loaded_module._extract_output_type( + {"text": SimpleNamespace(format=SimpleNamespace(type="text"))} + ) + == "text" + ) + # Invalid request shapes should degrade to no extracted output type rather + # than surfacing validation errors from instrumentation. + assert ( + loaded_module._extract_output_type({"text": {"format": "plain"}}) + is None + ) + assert loaded_module._extract_output_type({"text": "plain"}) is None + + +def test_extractors_handle_missing_genai_types_import(loaded_module): + with ( + mock.patch.object(loaded_module, "Text", None), + mock.patch.object(loaded_module, "InputMessage", None), + mock.patch.object(loaded_module, "OutputMessage", None), + ): + assert ( + loaded_module._extract_system_instruction({"instructions": "hi"}) + == [] + ) + assert loaded_module._extract_input_messages({"input": "hi"}) == [] + assert ( + loaded_module._extract_output_messages( + SimpleNamespace( + output=[SimpleNamespace(type="message", content=[])] + ) + ) + == [] + ) + + +def test_set_invocation_response_attributes_populates_usage_and_metadata( + loaded_module, + gen_ai_usage_cache_creation_input_tokens, + gen_ai_usage_cache_read_input_tokens, +): + invocation = LLMInvocation(request_model="gpt-4o-mini") + result = SimpleNamespace( + model="gpt-4.1", + id="resp_123", + service_tier="scale", + usage=SimpleNamespace( + prompt_tokens=11, + completion_tokens=7, + prompt_tokens_details=SimpleNamespace( + cached_tokens=3, + cache_creation_input_tokens=5, + ), + ), + ) + + loaded_module._set_invocation_response_attributes( + invocation, result, capture_content=False + ) + + assert invocation.response_model_name == "gpt-4.1" + assert invocation.response_id == "resp_123" + assert invocation.input_tokens == 11 + assert invocation.output_tokens == 7 + assert invocation.attributes == { + OpenAIAttributes.OPENAI_RESPONSE_SERVICE_TIER: "scale", + gen_ai_usage_cache_read_input_tokens: 3, + gen_ai_usage_cache_creation_input_tokens: 5, + } + + +def test_set_invocation_response_attributes_accepts_mapping_usage( + loaded_module, + gen_ai_usage_cache_creation_input_tokens, + gen_ai_usage_cache_read_input_tokens, +): + invocation = LLMInvocation(request_model="gpt-4o-mini") + result = SimpleNamespace( + usage={ + "input_tokens": 13, + "output_tokens": 8, + "input_tokens_details": { + "cached_tokens": 2, + "cache_creation_input_tokens": 4, + }, + } + ) + + loaded_module._set_invocation_response_attributes( + invocation, result, capture_content=False + ) + + assert invocation.input_tokens == 13 + assert invocation.output_tokens == 8 + assert invocation.attributes == { + gen_ai_usage_cache_read_input_tokens: 2, + gen_ai_usage_cache_creation_input_tokens: 4, + } + + +def test_set_invocation_response_attributes_populates_output_messages( + loaded_module, +): + invocation = LLMInvocation(request_model="gpt-4o-mini") + result = SimpleNamespace( + output=[ + SimpleNamespace( + type="message", + role="assistant", + status="completed", + content=[SimpleNamespace(type="output_text", text="Done")], + ) + ] + ) + + loaded_module._set_invocation_response_attributes( + invocation, result, capture_content=True + ) + + assert invocation.finish_reasons == ["stop"] + assert [ + (message.role, message.finish_reason) + for message in invocation.output_messages + ] == [("assistant", "stop")] + assert [ + [part.content for part in message.parts] + for message in invocation.output_messages + ] == [["Done"]] + + +def test_prevalidated_response_model_skips_revalidation( + loaded_module, monkeypatch +): + validated_result = _validate_compat_model( + loaded_module, + loaded_module._ResponsesResultModel, + SimpleNamespace( + output=[ + SimpleNamespace( + type="message", + status="completed", + content=[SimpleNamespace(type="output_text", text="Done")], + ) + ] + ), + ) + assert validated_result is not None + + def _unexpected_validation(_result): + raise AssertionError("unexpected response revalidation") + + monkeypatch.setattr( + loaded_module, "_validate_response_result", _unexpected_validation + ) + + assert loaded_module._extract_finish_reasons(validated_result) == ["stop"] + messages = loaded_module._extract_output_messages(validated_result) + assert [part.content for part in messages[0].parts] == ["Done"] + + +@pytest.mark.parametrize( + ("kwargs", "extractor_name"), + [ + ({"instructions": ["not-a-string"]}, "_extract_system_instruction"), + ({"input": 42}, "_extract_input_messages"), + ({"text": {"format": {"type": 42}}}, "_extract_output_type"), + ], +) +def test_request_validation_errors_are_logged_and_ignored( + loaded_module, caplog, kwargs, extractor_name +): + caplog.set_level(logging.DEBUG, logger=loaded_module.__name__) + extractor = getattr(loaded_module, extractor_name) + + result = extractor(kwargs) + + assert result in ([], None) + assert "OpenAI responses extractor validation failed" in caplog.text + + +def test_response_validation_errors_are_logged_and_ignored( + loaded_module, caplog +): + caplog.set_level(logging.DEBUG, logger=loaded_module.__name__) + invocation = LLMInvocation(request_model="gpt-4o-mini") + invalid_result = SimpleNamespace(output=42, usage=42) + + assert loaded_module._extract_output_messages(invalid_result) == [] + assert loaded_module._extract_finish_reasons(invalid_result) == [] + + loaded_module._set_invocation_response_attributes( + invocation, invalid_result, capture_content=True + ) + + assert invocation.response_model_name is None + assert invocation.response_id is None + assert invocation.input_tokens is None + assert invocation.output_tokens is None + assert invocation.finish_reasons is None + assert not invocation.output_messages + assert not invocation.attributes + assert "OpenAI responses extractor validation failed" in caplog.text diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_response_wrappers.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_response_wrappers.py new file mode 100644 index 0000000000..3061a23f3a --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_response_wrappers.py @@ -0,0 +1,458 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from types import SimpleNamespace + +import pytest + +from opentelemetry.instrumentation.openai_v2.response_wrappers import ( + AsyncResponseStreamManagerWrapper, + AsyncResponseStreamWrapper, + ResponseStreamManagerWrapper, + ResponseStreamWrapper, +) + + +class _FakeManager: + def __init__(self, stream, suppressed=False, exit_error=None): + self._stream = stream + self._suppressed = suppressed + self._exit_error = exit_error + self.exit_args = None + + def __enter__(self): + return self._stream + + def __exit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + if self._exit_error is not None: + raise self._exit_error + return self._suppressed + + +class _FakeAsyncManager: + def __init__(self, stream, suppressed=False, exit_error=None): + self._stream = stream + self._suppressed = suppressed + self._exit_error = exit_error + self.exit_args = None + + async def __aenter__(self): + return self._stream + + async def __aexit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + if self._exit_error is not None: + raise self._exit_error + return self._suppressed + + +def _noop_stop_llm(invocation): + del invocation + + +def _noop_fail_llm(invocation, error): + del invocation + del error + + +def _make_wrapper(manager): + handler = SimpleNamespace() + invocation = SimpleNamespace(request_model=None) + return ResponseStreamManagerWrapper( + manager=manager, + handler=handler, + invocation=invocation, + capture_content=False, + ) + + +def _make_stream_wrapper(stream, handler=None): + if handler is None: + handler = SimpleNamespace( + stop_llm=_noop_stop_llm, + fail_llm=_noop_fail_llm, + ) + invocation = SimpleNamespace(request_model=None) + return ResponseStreamWrapper( + stream=stream, + handler=handler, + invocation=invocation, + capture_content=False, + ) + + +def _make_async_manager_wrapper(manager): + handler = SimpleNamespace() + invocation = SimpleNamespace(request_model=None) + return AsyncResponseStreamManagerWrapper( + manager=manager, + handler=handler, + invocation=invocation, + capture_content=False, + ) + + +def _make_async_stream_wrapper(stream, handler=None): + if handler is None: + handler = SimpleNamespace( + stop_llm=_noop_stop_llm, + fail_llm=_noop_fail_llm, + ) + invocation = SimpleNamespace(request_model=None) + return AsyncResponseStreamWrapper( + stream=stream, + handler=handler, + invocation=invocation, + capture_content=False, + ) + + +class _FakeStreamWrapper: + def __init__(self): + self.exit_args = None + + def __exit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + return False + + +class _FakeAsyncStreamWrapper: + def __init__(self): + self.exit_args = None + + async def __aexit__(self, exc_type, exc_val, exc_tb): + self.exit_args = (exc_type, exc_val, exc_tb) + return False + + +class _FakeAsyncResponse: + def __init__(self): + self.aclose_calls = 0 + + async def aclose(self): + self.aclose_calls += 1 + + +class _FakeSyncResponse: + def __init__(self): + self.close_calls = 0 + + def close(self): + self.close_calls += 1 + + +class _FakeAsyncStream: + def __init__( + self, + *, + events=None, + error=None, + final_response=None, + response=None, + ): + self._events = list(events or []) + self._error = error + self._final_response = final_response + self._response = response + self.close_calls = 0 + self.get_final_response_calls = 0 + + async def __anext__(self): + if self._events: + return self._events.pop(0) + if self._error is not None: + raise self._error + raise StopAsyncIteration + + async def close(self): + self.close_calls += 1 + + async def get_final_response(self): + self.get_final_response_calls += 1 + return self._final_response + + +def test_manager_exit_forwards_exception_to_stream_wrapper(): + manager = _FakeManager(stream=SimpleNamespace(), suppressed=False) + wrapper = _make_wrapper(manager) + stream_wrapper = _FakeStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("boom") + result = wrapper.__exit__(ValueError, error, None) + + assert result is False + assert manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) + assert wrapper._stream_wrapper is None + + +def test_manager_exit_uses_none_exception_when_manager_suppresses(): + manager = _FakeManager(stream=SimpleNamespace(), suppressed=True) + wrapper = _make_wrapper(manager) + stream_wrapper = _FakeStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = RuntimeError("ignored") + result = wrapper.__exit__(RuntimeError, error, None) + + assert result is True + assert manager.exit_args == (RuntimeError, error, None) + assert stream_wrapper.exit_args == (None, None, None) + assert wrapper._stream_wrapper is None + + +def test_manager_exit_still_finalizes_stream_wrapper_when_manager_raises(): + manager_error = RuntimeError("manager failure") + manager = _FakeManager( + stream=SimpleNamespace(), suppressed=False, exit_error=manager_error + ) + wrapper = _make_wrapper(manager) + stream_wrapper = _FakeStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("outer") + with pytest.raises(RuntimeError, match="manager failure"): + wrapper.__exit__(ValueError, error, None) + + assert manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) + assert wrapper._stream_wrapper is None + + +def test_stream_wrapper_response_falls_back_to_public_response_attr(): + response = _FakeSyncResponse() + stream = SimpleNamespace(response=response) + wrapper = _make_stream_wrapper(stream) + stopped = [] + + wrapper._stop = stopped.append + + wrapper.response.close() + + assert response.close_calls == 1 + assert stopped == [None] + + +@pytest.mark.asyncio +async def test_async_manager_exit_forwards_exception_to_stream_wrapper(): + manager = _FakeAsyncManager(stream=SimpleNamespace(), suppressed=False) + wrapper = _make_async_manager_wrapper(manager) + stream_wrapper = _FakeAsyncStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("boom") + result = await wrapper.__aexit__(ValueError, error, None) + + assert result is False + assert manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) + assert wrapper._stream_wrapper is None + + +@pytest.mark.asyncio +async def test_async_manager_enter_constructs_async_stream_wrapper(): + stream = _FakeAsyncStream() + manager = _FakeAsyncManager(stream=stream) + wrapper = _make_async_manager_wrapper(manager) + + async with wrapper as result: + assert isinstance(result, AsyncResponseStreamWrapper) + assert result.stream is stream + assert wrapper._stream_wrapper is result + + +@pytest.mark.asyncio +async def test_async_manager_exit_uses_none_exception_when_manager_suppresses(): + manager = _FakeAsyncManager(stream=SimpleNamespace(), suppressed=True) + wrapper = _make_async_manager_wrapper(manager) + stream_wrapper = _FakeAsyncStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = RuntimeError("ignored") + result = await wrapper.__aexit__(RuntimeError, error, None) + + assert result is True + assert manager.exit_args == (RuntimeError, error, None) + assert stream_wrapper.exit_args == (None, None, None) + assert wrapper._stream_wrapper is None + + +@pytest.mark.asyncio +async def test_async_manager_exit_still_finalizes_stream_wrapper_when_manager_raises(): + manager_error = RuntimeError("manager failure") + manager = _FakeAsyncManager( + stream=SimpleNamespace(), suppressed=False, exit_error=manager_error + ) + wrapper = _make_async_manager_wrapper(manager) + stream_wrapper = _FakeAsyncStreamWrapper() + wrapper._stream_wrapper = stream_wrapper + + error = ValueError("outer") + with pytest.raises(RuntimeError, match="manager failure"): + await wrapper.__aexit__(ValueError, error, None) + + assert manager.exit_args == (ValueError, error, None) + assert stream_wrapper.exit_args == (ValueError, error, None) + assert wrapper._stream_wrapper is None + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_exit_closes_without_exception(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + + wrapper._stop = stopped.append + + result = await wrapper.__aexit__(None, None, None) + + assert result is False + assert stream.close_calls == 1 + assert stopped == [None] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_exit_fails_and_closes_on_exception(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + failures = [] + + def record_failure(message, error_type): + failures.append((message, error_type)) + + wrapper._stop = stopped.append + wrapper._fail = record_failure + + error = ValueError("boom") + result = await wrapper.__aexit__(ValueError, error, None) + + assert result is False + assert stream.close_calls == 1 + assert stopped == [None] + assert failures == [("boom", ValueError)] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_close_closes_stream_and_stops(): + stream = _FakeAsyncStream() + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + + wrapper._stop = stopped.append + + await wrapper.close() + + assert stream.close_calls == 1 + assert stopped == [None] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_processes_events_and_stops_on_completion(): + event = SimpleNamespace(type="response.created") + stream = _FakeAsyncStream(events=[event]) + wrapper = _make_async_stream_wrapper(stream) + processed = [] + stopped = [] + + wrapper.process_event = processed.append + wrapper._stop = stopped.append + + result = await anext(wrapper) + + assert result is event + assert processed == [event] + + with pytest.raises(StopAsyncIteration): + await anext(wrapper) + + assert stopped == [None] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_until_done_consumes_stream(): + events = [ + SimpleNamespace(type="response.created"), + SimpleNamespace(type="response.in_progress"), + ] + stream = _FakeAsyncStream(events=events) + wrapper = _make_async_stream_wrapper(stream) + processed = [] + stopped = [] + + wrapper.process_event = processed.append + wrapper._stop = stopped.append + + result = await wrapper.until_done() + + assert result is wrapper + assert processed == events + assert stopped == [None] + + +@pytest.mark.asyncio +async def test_async_stream_wrapper_fails_and_reraises_stream_errors(): + error = ValueError("boom") + stream = _FakeAsyncStream(error=error) + wrapper = _make_async_stream_wrapper(stream) + failures = [] + + def record_failure(message, error_type): + failures.append((message, error_type)) + + wrapper._fail = record_failure + + with pytest.raises(ValueError, match="boom"): + await anext(wrapper) + + assert failures == [("boom", ValueError)] + + +@pytest.mark.asyncio +async def test_async_stream_response_aclose_finalizes_wrapper(): + response = _FakeAsyncResponse() + stream = _FakeAsyncStream(response=response) + wrapper = _make_async_stream_wrapper(stream) + stopped = [] + + wrapper._stop = stopped.append + + await wrapper.response.aclose() + + assert response.aclose_calls == 1 + assert stopped == [None] + + +@pytest.mark.asyncio +async def test_async_stream_response_is_none_when_stream_has_no_response(): + wrapper = _make_async_stream_wrapper(SimpleNamespace()) + + assert wrapper.response is None + + +@pytest.mark.asyncio +async def test_async_stream_get_final_response_waits_for_completion(): + event = SimpleNamespace(type="response.in_progress") + final_response = SimpleNamespace(id="response_123") + stream = _FakeAsyncStream(events=[event], final_response=final_response) + wrapper = _make_async_stream_wrapper(stream) + + wrapper.process_event = lambda current: current + + result = await wrapper.get_final_response() + + assert result is final_response + assert stream.get_final_response_calls == 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-vertexai/pyproject.toml index 3d715a2a87..91a5895698 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Official VertexAI instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,10 +25,10 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-api ~= 1.37", - "opentelemetry-instrumentation ~= 0.58b0", - "opentelemetry-semantic-conventions ~= 0.58b0", - "opentelemetry-util-genai >= 0.2b0, <0.4b0", + "opentelemetry-api ~= 1.39", + "opentelemetry-instrumentation ~= 0.60b0", + "opentelemetry-semantic-conventions ~= 0.60b0", + "opentelemetry-util-genai >= 0.4b0.dev, <0.5b0", ] [project.optional-dependencies] diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/utils.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/utils.py index 3221cdf9bc..9686d0dd7a 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/utils.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/utils.py @@ -52,12 +52,14 @@ ) from opentelemetry.semconv.attributes import server_attributes from opentelemetry.util.genai.types import ( + Blob, ContentCapturingMode, FinishReason, MessagePart, Text, - ToolCall, + ToolCallRequest, ToolCallResponse, + Uri, ) from opentelemetry.util.genai.utils import get_content_capturing_mode from opentelemetry.util.types import AnyValue, AttributeValue @@ -308,21 +310,15 @@ def request_to_events( yield user_event(role=content.role, content=request_content) -@dataclass -class BlobPart: - data: bytes - mime_type: str - type: Literal["blob"] = "blob" - - -@dataclass -class FileDataPart: - mime_type: str - uri: str - type: Literal["file_data"] = "file_data" - - class Config: - extra = "allow" +def _modality_from_mime_type(mime_type: str) -> str: + """Infer modality from MIME type prefix.""" + if mime_type.startswith("image/"): + return "image" + if mime_type.startswith("video/"): + return "video" + if mime_type.startswith("audio/"): + return "audio" + return mime_type def convert_content_to_message_parts( @@ -341,7 +337,7 @@ def convert_content_to_message_parts( elif "function_call" in part: part = part.function_call parts.append( - ToolCall( + ToolCallRequest( id=f"{part.name}_{idx}", name=part.name, arguments=json_format.MessageToDict( @@ -353,14 +349,22 @@ def convert_content_to_message_parts( parts.append(Text(content=part.text)) elif "inline_data" in part: part = part.inline_data + mime_type = part.mime_type or "" parts.append( - BlobPart(mime_type=part.mime_type or "", data=part.data or b"") + Blob( + mime_type=mime_type, + modality=_modality_from_mime_type(mime_type), + content=part.data or b"", + ) ) elif "file_data" in part: part = part.file_data + mime_type = part.mime_type or "" parts.append( - FileDataPart( - mime_type=part.mime_type or "", uri=part.file_uri or "" + Uri( + mime_type=mime_type, + modality=_modality_from_mime_type(mime_type), + uri=part.file_uri or "", ) ) else: diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.latest.txt b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.latest.txt index 74005b7afb..a818affa54 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.latest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.latest.txt @@ -61,7 +61,7 @@ importlib_metadata==8.5.0 iniconfig==2.0.0 multidict==6.1.0 packaging==24.2 -pluggy==1.5.0 +pluggy==1.6.0 propcache==0.2.0 proto-plus==1.25.0 protobuf==5.29.1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.oldest.txt b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.oldest.txt index 7bfd62ff5f..d02e3dc8e4 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.oldest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/requirements.oldest.txt @@ -39,7 +39,7 @@ importlib-metadata==6.11.0 iniconfig==2.0.0 multidict==6.1.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 propcache==0.2.0 proto-plus==1.25.0 protobuf==5.29.1 @@ -65,11 +65,12 @@ grpcio>=1.75.1 ; python_version >= "3.14" shapely==2.0.6 ; python_version < "3.10" shapely==2.1.2 ; python_version >= "3.10" # when updating, also update in pyproject.toml -opentelemetry-api==1.37 -opentelemetry-sdk==1.37 -opentelemetry-semantic-conventions==0.58b0 -opentelemetry-instrumentation==0.58b0 -opentelemetry-util-genai[upload]==0.2b0 +opentelemetry-api==1.39 +opentelemetry-sdk==1.39 +opentelemetry-semantic-conventions==0.60b0 +opentelemetry-instrumentation==0.60b0 +# opentelemetry-util-genai[upload]==0.2b0 +-e util/opentelemetry-util-genai[upload] fsspec==2025.9.0 -e instrumentation-genai/opentelemetry-instrumentation-vertexai[instruments] diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py index 5613f551a6..617d9a81c9 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py @@ -74,7 +74,7 @@ def test_generate_content_with_files( "gen_ai.usage.output_tokens": 5, "server.address": "us-central1-aiplatform.googleapis.com", "server.port": 443, - "gen_ai.input.messages": '[{"role":"user","parts":[{"content":"Say this is a test","type":"text"},{"mime_type":"image/jpeg","uri":"https://images.pdimagearchive.org/collections/microscopic-delights/1lede-0021.jpg","type":"file_data"},{"data":"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==","mime_type":"image/jpeg","type":"blob"}]}]', + "gen_ai.input.messages": '[{"role":"user","parts":[{"content":"Say this is a test","type":"text"},{"mime_type":"image/jpeg","modality":"image","uri":"https://images.pdimagearchive.org/collections/microscopic-delights/1lede-0021.jpg","type":"uri"},{"mime_type":"image/jpeg","modality":"image","content":"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==","type":"blob"}]}]', "gen_ai.output.messages": '[{"role":"model","parts":[{"content":"This is a test.","type":"text"}],"finish_reason":"stop"}]', } @@ -97,12 +97,14 @@ def test_generate_content_with_files( {"content": "Say this is a test", "type": "text"}, { "mime_type": "image/jpeg", + "modality": "image", "uri": "https://images.pdimagearchive.org/collections/microscopic-delights/1lede-0021.jpg", - "type": "file_data", + "type": "uri", }, { - "data": b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x05\x00\x00\x00\x05\x08\x06\x00\x00\x00\x8do&\xe5\x00\x00\x00\x1cIDAT\x08\xd7c\xf8\xff\xff?\xc3\x7f\x06 \x05\xc3 \x12\x84\xd01\xf1\x82X\xcd\x04\x00\x0e\xf55\xcb\xd1\x8e\x0e\x1f\x00\x00\x00\x00IEND\xaeB`\x82", "mime_type": "image/jpeg", + "modality": "image", + "content": b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x05\x00\x00\x00\x05\x08\x06\x00\x00\x00\x8do&\xe5\x00\x00\x00\x1cIDAT\x08\xd7c\xf8\xff\xff?\xc3\x7f\x06 \x05\xc3 \x12\x84\xd01\xf1\x82X\xcd\x04\x00\x0e\xf55\xcb\xd1\x8e\x0e\x1f\x00\x00\x00\x00IEND\xaeB`\x82", "type": "blob", }, ), diff --git a/instrumentation-genai/opentelemetry-instrumentation-weaviate/pyproject.toml b/instrumentation-genai/opentelemetry-instrumentation-weaviate/pyproject.toml index 2489c37895..7ff31901de 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-weaviate/pyproject.toml +++ b/instrumentation-genai/opentelemetry-instrumentation-weaviate/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Official Weaviate Client Instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/instrumentation/README.md b/instrumentation/README.md index 27008c0015..8d9a247945 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -11,13 +11,12 @@ | [opentelemetry-instrumentation-asyncio](./opentelemetry-instrumentation-asyncio) | asyncio | No | development | [opentelemetry-instrumentation-asyncpg](./opentelemetry-instrumentation-asyncpg) | asyncpg >= 0.12.0 | No | development | [opentelemetry-instrumentation-aws-lambda](./opentelemetry-instrumentation-aws-lambda) | aws_lambda | No | development -| [opentelemetry-instrumentation-boto](./opentelemetry-instrumentation-boto) | boto~=2.0 | No | development | [opentelemetry-instrumentation-boto3sqs](./opentelemetry-instrumentation-boto3sqs) | boto3 ~= 1.0 | No | development -| [opentelemetry-instrumentation-botocore](./opentelemetry-instrumentation-botocore) | botocore ~= 1.0 | No | development +| [opentelemetry-instrumentation-botocore](./opentelemetry-instrumentation-botocore) | botocore~=1.0,aiobotocore~=2.0 | No | development | [opentelemetry-instrumentation-cassandra](./opentelemetry-instrumentation-cassandra) | cassandra-driver ~= 3.25,scylla-driver ~= 3.25 | No | development | [opentelemetry-instrumentation-celery](./opentelemetry-instrumentation-celery) | celery >= 4.0, < 6.0 | No | development | [opentelemetry-instrumentation-click](./opentelemetry-instrumentation-click) | click >= 8.1.3, < 9.0.0 | No | development -| [opentelemetry-instrumentation-confluent-kafka](./opentelemetry-instrumentation-confluent-kafka) | confluent-kafka >= 1.8.2, <= 2.13.0 | No | development +| [opentelemetry-instrumentation-confluent-kafka](./opentelemetry-instrumentation-confluent-kafka) | confluent-kafka >= 1.8.2, < 3.0.0 | No | development | [opentelemetry-instrumentation-dbapi](./opentelemetry-instrumentation-dbapi) | dbapi | No | development | [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 2.0 | Yes | development | [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 6.0 | No | development @@ -42,7 +41,7 @@ | [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No | development | [opentelemetry-instrumentation-remoulade](./opentelemetry-instrumentation-remoulade) | remoulade >= 0.50 | No | development | [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes | migration -| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy >= 1.0.0, < 2.1.0 | Yes | development +| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy >= 1.0.0, < 2.1.0 | Yes | migration | [opentelemetry-instrumentation-sqlite3](./opentelemetry-instrumentation-sqlite3) | sqlite3 | No | development | [opentelemetry-instrumentation-starlette](./opentelemetry-instrumentation-starlette) | starlette >= 0.13 | Yes | development | [opentelemetry-instrumentation-system-metrics](./opentelemetry-instrumentation-system-metrics) | psutil >= 5 | No | development diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aio-pika/pyproject.toml index 0f777b75e4..2f57713d0d 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Aio-pika instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.5", - "opentelemetry-instrumentation == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/version.py b/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/version.py +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-0.txt index c2847c672c..61110dde10 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-0.txt @@ -7,7 +7,7 @@ iniconfig==2.0.0 multidict==6.0.5 packaging==24.0 pamqp==3.1.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-1.txt index 7c12b25ea4..f3d7fa41a8 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-1.txt @@ -7,7 +7,7 @@ iniconfig==2.0.0 multidict==6.0.5 packaging==24.0 pamqp==3.2.1 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt index 3a4c21b3f7..24e7c6cdaa 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-2.txt @@ -7,7 +7,7 @@ iniconfig==2.0.0 multidict==6.0.5 packaging==24.0 pamqp==3.2.1 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt index f5b8fdf345..12f66a0308 100644 --- a/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt +++ b/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-3.txt @@ -7,7 +7,7 @@ iniconfig==2.0.0 multidict==6.0.5 packaging==24.0 pamqp==3.3.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml index 8f2c02638b..f908184beb 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry aiohttp client instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/version.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/version.py index 1fbc44b3e3..edf0d60188 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/version.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-aiohttp-client/test-requirements.txt index 55ddf646dc..335548eb3c 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/test-requirements.txt @@ -17,7 +17,7 @@ Jinja2==3.1.6 MarkupSafe==2.1.5 multidict==6.0.5 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 requests==2.32.3 diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aiohttp-server/pyproject.toml index ce36611a33..6c1d86d8fa 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-server/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Aiohttp server instrumentation for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io"} ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/version.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/version.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt index d492daf3c6..cd3dee92a0 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt @@ -8,7 +8,7 @@ idna==3.7 iniconfig==2.0.0 multidict==6.0.5 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytest-aiohttp==1.0.5 diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aiokafka/pyproject.toml index 3bd3cc2551..52714ef384 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry aiokafka instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.27", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", "typing_extensions ~= 4.1", ] diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/utils.py b/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/utils.py index 36bfb29efa..8533a98858 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/utils.py +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/utils.py @@ -162,9 +162,17 @@ async def _extract_send_partition( key = _extract_send_key(args, kwargs) value = _extract_send_value(args, kwargs) partition = _extract_argument("partition", 3, None, args, kwargs) - key_bytes, value_bytes = cast( - "tuple[bytes | None, bytes | None]", - instance._serialize(topic, key, value), # type: ignore[reportUnknownMemberType] + key_bytes = cast( + "bytes | None", + instance._key_serializer(key) # type: ignore[reportUnknownMemberType] + if instance._key_serializer # type: ignore[reportUnknownMemberType] + else key, + ) + value_bytes = cast( + "bytes | None", + instance._value_serializer(value) # type: ignore[reportUnknownMemberType] + if instance._value_serializer # type: ignore[reportUnknownMemberType] + else value, ) valid_types = (bytes, bytearray, memoryview, type(None)) if ( diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/version.py b/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/version.py +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-aiokafka/test-requirements.txt index 460e58e091..c29f54a732 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/test-requirements.txt @@ -1,4 +1,5 @@ -aiokafka==0.11.0 +aiokafka==0.13.0; python_version >= "3.10" +aiokafka==0.12.0; python_version < "3.10" pytest==7.4.4 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-aiokafka diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py index 8905c2a68e..e0812fe1c5 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py @@ -126,7 +126,7 @@ async def consumer_factory(**consumer_kwargs: Any) -> AIOKafkaConsumer: @staticmethod async def producer_factory() -> AIOKafkaProducer: - producer = AIOKafkaProducer(api_version="1.0") + producer = AIOKafkaProducer() producer.client._wait_on_metadata = mock.AsyncMock() producer.client.bootstrap = mock.AsyncMock() @@ -261,21 +261,12 @@ async def async_consume_hook(span, *_) -> None: async def test_getone_consume_hook(self) -> None: async_consume_hook_mock = mock.AsyncMock() - def is_async_consume_hook_mock(obj: Any) -> bool: - return obj is async_consume_hook_mock - AIOKafkaInstrumentor().uninstrument() - # TODO: remove mock.patch when we drop Python 3.9 support - with mock.patch( - "opentelemetry.instrumentation.aiokafka.iscoroutinefunction" - ) as iscoro: - iscoro.side_effect = is_async_consume_hook_mock - - AIOKafkaInstrumentor().instrument( - tracer_provider=self.tracer_provider, - async_consume_hook=async_consume_hook_mock, - ) + AIOKafkaInstrumentor().instrument( + tracer_provider=self.tracer_provider, + async_consume_hook=async_consume_hook_mock, + ) consumer = await self.consumer_factory() self.addAsyncCleanup(consumer.stop) @@ -458,20 +449,11 @@ async def test_send_baggage(self) -> None: async def test_send_produce_hook(self) -> None: async_produce_hook_mock = mock.AsyncMock() - def is_async_produce_hook_mock(obj: Any) -> bool: - return obj is async_produce_hook_mock - AIOKafkaInstrumentor().uninstrument() - # TODO: remove mock.patch when we drop Python 3.9 support - with mock.patch( - "opentelemetry.instrumentation.aiokafka.iscoroutinefunction" - ) as iscoro: - iscoro.side_effect = is_async_produce_hook_mock - - AIOKafkaInstrumentor().instrument( - tracer_provider=self.tracer_provider, - async_produce_hook=async_produce_hook_mock, - ) + AIOKafkaInstrumentor().instrument( + tracer_provider=self.tracer_provider, + async_produce_hook=async_produce_hook_mock, + ) producer = await self.producer_factory() self.addAsyncCleanup(producer.stop) @@ -498,37 +480,43 @@ async def test_send_and_wait(self) -> None: AIOKafkaInstrumentor().instrument(tracer_provider=self.tracer_provider) producer = await self.producer_factory() - add_message_mock: mock.AsyncMock = ( - producer._message_accumulator.add_message - ) - add_message_mock.side_effect = [mock.AsyncMock()(), mock.AsyncMock()()] - - tracer = self.tracer_provider.get_tracer(__name__) - with tracer.start_as_current_span("test_span") as span: - await producer.send_and_wait("topic_1", b"value_1") + try: + add_message_mock: mock.AsyncMock = ( + producer._message_accumulator.add_message + ) + add_message_mock.side_effect = [ + mock.AsyncMock()(), + mock.AsyncMock()(), + ] - add_message_mock.assert_awaited_with( - TopicPartition(topic="topic_1", partition=1), - None, - b"value_1", - 40.0, - timestamp_ms=None, - headers=[("traceparent", mock.ANY)], - ) - assert ( - add_message_mock.call_args_list[0] - .kwargs["headers"][0][1] - .startswith( - f"00-{format_trace_id(span.get_span_context().trace_id)}-".encode() + tracer = self.tracer_provider.get_tracer(__name__) + with tracer.start_as_current_span("test_span") as span: + await producer.send_and_wait("topic_1", b"value_1") + + add_message_mock.assert_awaited_with( + TopicPartition(topic="topic_1", partition=1), + None, + b"value_1", + 40.0, + timestamp_ms=None, + headers=[("traceparent", mock.ANY)], + ) + assert ( + add_message_mock.call_args_list[0] + .kwargs["headers"][0][1] + .startswith( + f"00-{format_trace_id(span.get_span_context().trace_id)}-".encode() + ) ) - ) - await producer.send_and_wait("topic_2", b"value_2") - add_message_mock.assert_awaited_with( - TopicPartition(topic="topic_2", partition=1), - None, - b"value_2", - 40.0, - timestamp_ms=None, - headers=[("traceparent", mock.ANY)], - ) + await producer.send_and_wait("topic_2", b"value_2") + add_message_mock.assert_awaited_with( + TopicPartition(topic="topic_2", partition=1), + None, + b"value_2", + 40.0, + timestamp_ms=None, + headers=[("traceparent", mock.ANY)], + ) + finally: + await producer.stop() diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_utils.py b/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_utils.py index a7dcbe6dcf..14e6d36178 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_utils.py +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_utils.py @@ -256,7 +256,7 @@ async def test_wrap_getmany( extract_bootstrap_servers: mock.MagicMock, _enrich_getmany_poll_span: mock.MagicMock, _enrich_getmany_topic_span: mock.MagicMock, - _create_consumer_span: mock.MagicMock, + _create_consumer_span: mock.AsyncMock, extract: mock.MagicMock, ) -> None: tracer = mock.MagicMock() @@ -270,6 +270,7 @@ async def test_wrap_getmany( } ) kafka_consumer = mock.MagicMock() + _create_consumer_span.return_value = mock.MagicMock() wrapped_getmany = _wrap_getmany(tracer, consume_hook) records = await wrapped_getmany( @@ -370,7 +371,8 @@ async def test_create_consumer_span( async def test_kafka_properties_extractor(self): aiokafka_instance_mock = mock.Mock() - aiokafka_instance_mock._serialize.return_value = None, None + aiokafka_instance_mock._key_serializer = None + aiokafka_instance_mock._value_serializer = None aiokafka_instance_mock._partition.return_value = "partition" aiokafka_instance_mock.client._wait_on_metadata = mock.AsyncMock() assert ( diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aiopg/pyproject.toml index 204f10ecc8..37327d50ed 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-aiopg/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry aiopg instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py index 948050818c..c2e3e7523c 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py @@ -11,7 +11,7 @@ from opentelemetry.trace import SpanKind -# pylint: disable=abstract-method +# pylint: disable=abstract-method,no-member class AsyncProxyObject(wrapt.ObjectProxy): def __aiter__(self): return self.__wrapped__.__aiter__() @@ -54,7 +54,7 @@ async def wrapped_pool(self, create_pool_method, args, kwargs): def get_traced_connection_proxy( connection, db_api_integration, *args, **kwargs ): - # pylint: disable=abstract-method + # pylint: disable=abstract-method,no-member class TracedConnectionProxy(AsyncProxyObject): # pylint: disable=unused-argument def __init__(self, connection, *args, **kwargs): @@ -73,7 +73,7 @@ async def _cursor(self, *args, **kwargs): def get_traced_pool_proxy(pool, db_api_integration, *args, **kwargs): - # pylint: disable=abstract-method + # pylint: disable=abstract-method,no-member class TracedPoolProxy(AsyncProxyObject): # pylint: disable=unused-argument def __init__(self, pool, *args, **kwargs): @@ -125,7 +125,7 @@ async def traced_execution( def get_traced_cursor_proxy(cursor, db_api_integration, *args, **kwargs): _traced_cursor = AsyncCursorTracer(db_api_integration) - # pylint: disable=abstract-method + # pylint: disable=abstract-method,no-member class AsyncCursorTracerProxy(AsyncProxyObject): # pylint: disable=unused-argument def __init__(self, cursor, *args, **kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/version.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/version.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt1.txt new file mode 100644 index 0000000000..ae191f8ee5 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt2.txt new file mode 100644 index 0000000000..01c75bb94f --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements.txt index a3d94d65ee..d40176d9f0 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements.txt @@ -1,16 +1,14 @@ aiopg==1.4.0 asgiref==3.8.1 async-timeout==4.0.3 -Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 psycopg2-binary==2.9.10 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-dbapi diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py index ab1fe53843..e4cbd8ef41 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py @@ -472,7 +472,7 @@ def test_instrument_connection(self): connection2 = wrappers.instrument_connection( self.tracer, connection, "-" ) - self.assertIs(connection2.__wrapped__, connection) + self.assertIs(connection2.__wrapped__, connection) # pylint: disable=no-member def test_uninstrument_connection(self): connection = mock.Mock() @@ -482,7 +482,7 @@ def test_uninstrument_connection(self): connection2 = wrappers.instrument_connection( self.tracer, connection, "-" ) - self.assertIs(connection2.__wrapped__, connection) + self.assertIs(connection2.__wrapped__, connection) # pylint: disable=no-member connection3 = wrappers.uninstrument_connection(connection2) self.assertIs(connection3, connection) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/pyproject.toml b/instrumentation/opentelemetry-instrumentation-asgi/pyproject.toml index 982de74d46..36ffeba0bc 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-asgi/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "ASGI instrumentation for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -28,9 +27,9 @@ classifiers = [ dependencies = [ "asgiref ~= 3.0", "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index 59c5083ada..8e7106010f 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -255,7 +255,10 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): from opentelemetry.instrumentation.propagators import ( get_global_response_propagator, ) -from opentelemetry.instrumentation.utils import _start_internal_or_server_span +from opentelemetry.instrumentation.utils import ( + _start_internal_or_server_span, + is_http_instrumentation_enabled, +) from opentelemetry.metrics import get_meter from opentelemetry.propagators.textmap import Getter, Setter from opentelemetry.semconv._incubating.attributes.http_attributes import ( @@ -583,7 +586,7 @@ class OpenTelemetryMiddleware: exclude_spans: Optionally exclude HTTP `send` and/or `receive` spans from the trace. """ - # pylint: disable=too-many-branches + # pylint: disable=too-many-branches,too-many-positional-arguments def __init__( self, app, @@ -745,7 +748,10 @@ async def __call__( send: An awaitable callable taking a single dictionary as argument. """ start = default_timer() - if scope["type"] not in ("http", "websocket"): + if not is_http_instrumentation_enabled() or scope["type"] not in ( + "http", + "websocket", + ): return await self.app(scope, receive, send) _, _, url = get_host_port_url_tuple(scope) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/version.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/version.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-asgi/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-asgi/test-requirements.txt index 4b87261ffb..b41ffa06c0 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-asgi/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py index f716c9f2f1..b14f207d8a 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py @@ -37,6 +37,7 @@ get_global_response_propagator, set_global_response_propagator, ) +from opentelemetry.instrumentation.utils import suppress_http_instrumentation from opentelemetry.sdk import resources from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -1880,6 +1881,19 @@ async def test_no_excluded_urls(self): spans = self.get_finished_spans() self.assertGreater(len(spans), 0) + async def test_suppress_http_instrumentation(self): + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) + + async def suppression_wrapper(scope, receive, send): + with suppress_http_instrumentation(): + await app(scope, receive, send) + + self.seed_app(suppression_wrapper) + await self.send_default_request() + await self.get_all_output() + spans = self.get_finished_spans() + self.assertEqual(len(spans), 0) + class TestAsgiAttributes(unittest.TestCase): def setUp(self): diff --git a/instrumentation/opentelemetry-instrumentation-asyncclick/pyproject.toml b/instrumentation/opentelemetry-instrumentation-asyncclick/pyproject.toml index ffc9999d12..16cc529965 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncclick/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-asyncclick/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Async Click instrumentation for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", "wrapt ~= 1.0", "typing_extensions ~= 4.12", ] diff --git a/instrumentation/opentelemetry-instrumentation-asyncclick/src/opentelemetry/instrumentation/asyncclick/version.py b/instrumentation/opentelemetry-instrumentation-asyncclick/src/opentelemetry/instrumentation/asyncclick/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncclick/src/opentelemetry/instrumentation/asyncclick/version.py +++ b/instrumentation/opentelemetry-instrumentation-asyncclick/src/opentelemetry/instrumentation/asyncclick/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/pyproject.toml b/instrumentation/opentelemetry-instrumentation-asyncio/pyproject.toml index 16917716fe..c1a42b4ed1 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-asyncio/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry instrumentation for asyncio" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.14", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/version.py b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/version.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt index 4f1877dd43..203aeebb0a 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytest-asyncio==0.23.5 diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py index f964044fc7..a89134ca9b 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_anext.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. import asyncio -import sys -from unittest import skipIf from unittest.mock import patch # pylint: disable=no-name-in-module @@ -44,9 +42,6 @@ def tearDown(self): # Asyncio anext() does not have __name__ attribute, which is used to determine if the coroutine should be traced. # This test is to ensure that the instrumentation does not break when the coroutine does not have __name__ attribute. # Additionally, ensure the coroutine is actually awaited. - @skipIf( - sys.version_info < (3, 10), "anext is only available in Python 3.10+" - ) def test_asyncio_anext(self): async def main(): async def async_gen(): diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_run_coroutine_threadsafe.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_run_coroutine_threadsafe.py index d612076dbf..fdf4bcb353 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_run_coroutine_threadsafe.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_run_coroutine_threadsafe.py @@ -13,9 +13,7 @@ # limitations under the License. import asyncio import threading -from concurrent.futures import ( # pylint: disable=no-name-in-module; TODO #4199 - ThreadPoolExecutor, -) +from concurrent.futures import ThreadPoolExecutor from unittest.mock import patch # pylint: disable=no-name-in-module diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/pyproject.toml b/instrumentation/opentelemetry-instrumentation-asyncpg/pyproject.toml index adb72936a3..62b83f4b76 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry instrumentation for AsyncPG" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/version.py b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/version.py +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt1.txt new file mode 100644 index 0000000000..ae191f8ee5 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt2.txt new file mode 100644 index 0000000000..01c75bb94f --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt index b2b4401d6b..f0dd93d268 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt @@ -1,15 +1,13 @@ asgiref==3.8.1 async-timeout==4.0.3 asyncpg==0.30.0 -Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-asyncpg diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py b/instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py index 0fc44d6a23..f8c905a2c7 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py @@ -3,7 +3,12 @@ import pytest from asyncpg import Connection, Record, cursor -from wrapt import ObjectProxy + +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy from opentelemetry import trace as trace_api from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor @@ -50,7 +55,7 @@ def assert_wrapped(assert_fnc): for method_name in methods: method = getattr(cls, method_name, None) assert_fnc( - isinstance(method, ObjectProxy), + isinstance(method, BaseObjectProxy), f"{method} isinstance {type(method)}", ) @@ -101,7 +106,7 @@ async def exec_mock(*args, **kwargs): ) with pytest.raises(StopAsyncIteration): - asyncio.run(crs_iter.__anext__()) + asyncio.run(anext(crs_iter)) spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 2) diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/pyproject.toml b/instrumentation/opentelemetry-instrumentation-aws-lambda/pyproject.toml index 3afe9b2131..e73c3f9926 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry AWS Lambda instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,8 +25,8 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", "opentelemetry-propagator-aws-xray ~= 1.0", ] diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/version.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/version.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-aws-lambda/test-requirements.txt index 75c3a56b62..1cf679a579 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-boto/README.rst b/instrumentation/opentelemetry-instrumentation-boto/README.rst deleted file mode 100644 index 86472c1bc6..0000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/README.rst +++ /dev/null @@ -1,24 +0,0 @@ -OpenTelemetry Boto Tracing -========================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-boto.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-boto/ - -This library allows tracing requests made by the Boto library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-boto - - -References ----------- - -* `OpenTelemetry Boto Tracing `_ -* `OpenTelemetry Project `_ -* `OpenTelemetry Python Examples `_ diff --git a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py b/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py deleted file mode 100644 index ca0218fe9f..0000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Instrument `Boto`_ to trace service requests. - -There are two options for instrumenting code. The first option is to use the -``opentelemetry-instrument`` executable which will automatically -instrument your Boto client. The second is to programmatically enable -instrumentation via the following code: - -.. _boto: https://pypi.org/project/boto/ - -Usage ------ - -.. code:: python - - from opentelemetry.instrumentation.boto import BotoInstrumentor - import boto - - - # Instrument Boto - BotoInstrumentor().instrument() - - # This will create a span with Boto-specific attributes - ec2 = boto.ec2.connect_to_region("us-west-2") - ec2.get_all_instances() - -API ---- -""" - -import logging -from inspect import currentframe -from typing import Collection - -from boto.connection import AWSAuthConnection, AWSQueryConnection -from wrapt import wrap_function_wrapper - -from opentelemetry.instrumentation.boto.package import _instruments -from opentelemetry.instrumentation.boto.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.semconv._incubating.attributes.http_attributes import ( - HTTP_METHOD, - HTTP_STATUS_CODE, -) -from opentelemetry.trace import SpanKind, get_tracer - -logger = logging.getLogger(__name__) - -SERVICE_PARAMS_BLOCK_LIST = {"s3": ["params.Body"]} - - -def _get_instance_region_name(instance): - region = getattr(instance, "region", None) - - if not region: - return None - if isinstance(region, str): - return region.split(":")[1] - return region.name - - -class BotoInstrumentor(BaseInstrumentor): - """A instrumentor for Boto - - See `BaseInstrumentor` - """ - - def __init__(self): - super().__init__() - self._original_boto = None - - def instrumentation_dependencies(self) -> Collection[str]: - return _instruments - - def _instrument(self, **kwargs): - # AWSQueryConnection and AWSAuthConnection are two different classes - # called by different services for connection. - # For example EC2 uses AWSQueryConnection and S3 uses - # AWSAuthConnection - - # pylint: disable=attribute-defined-outside-init - self._tracer = get_tracer( - __name__, - __version__, - kwargs.get("tracer_provider"), - schema_url="https://opentelemetry.io/schemas/1.11.0", - ) - - wrap_function_wrapper( - "boto.connection", - "AWSQueryConnection.make_request", - self._patched_query_request, - ) - wrap_function_wrapper( - "boto.connection", - "AWSAuthConnection.make_request", - self._patched_auth_request, - ) - - def _uninstrument(self, **kwargs): - unwrap(AWSQueryConnection, "make_request") - unwrap(AWSAuthConnection, "make_request") - - def _common_request( # pylint: disable=too-many-locals - self, - args_name, - traced_args, - operation_name, - original_func, - instance, - args, - kwargs, - ): - endpoint_name = getattr(instance, "host").split(".")[0] - - with self._tracer.start_as_current_span( - f"{endpoint_name}.command", - kind=SpanKind.CONSUMER, - ) as span: - span.set_attribute("endpoint", endpoint_name) - if args: - http_method = args[0] - span.set_attribute("http_method", http_method.lower()) - - # Original func returns a boto.connection.HTTPResponse object - result = original_func(*args, **kwargs) - - if span.is_recording(): - add_span_arg_tags( - span, - endpoint_name, - args, - args_name, - traced_args, - ) - - # Obtaining region name - region_name = _get_instance_region_name(instance) - - meta = { - "aws.agent": "boto", - "aws.operation": operation_name, - } - if region_name: - meta["aws.region"] = region_name - - for key, value in meta.items(): - span.set_attribute(key, value) - - span.set_attribute(HTTP_STATUS_CODE, getattr(result, "status")) - span.set_attribute(HTTP_METHOD, getattr(result, "_method")) - - return result - - def _patched_query_request(self, original_func, instance, args, kwargs): - return self._common_request( - ("operation_name", "params", "path", "verb"), - ["operation_name", "params", "path"], - args[0] if args else None, - original_func, - instance, - args, - kwargs, - ) - - def _patched_auth_request(self, original_func, instance, args, kwargs): - frame = currentframe().f_back - operation_name = None - while frame: - if frame.f_code.co_name == "make_request": - operation_name = frame.f_back.f_code.co_name - break - frame = frame.f_back - - return self._common_request( - ( - "method", - "path", - "headers", - "data", - "host", - "auth_path", - "sender", - ), - ["path", "data", "host"], - operation_name, - original_func, - instance, - args, - kwargs, - ) - - -def flatten_dict(dict_, sep=".", prefix=""): - """ - Returns a normalized dict of depth 1 with keys in order of embedding - """ - # NOTE: This should probably be in `opentelemetry.instrumentation.utils`. - # adapted from https://stackoverflow.com/a/19647596 - return ( - { - prefix + sep + k if prefix else k: v - for kk, vv in dict_.items() - for k, v in flatten_dict(vv, sep, kk).items() - } - if isinstance(dict_, dict) - else {prefix: dict_} - ) - - -def add_span_arg_tags(span, aws_service, args, args_names, args_traced): - def truncate_arg_value(value, max_len=1024): - """Truncate values which are bytes and greater than `max_len`. - Useful for parameters like "Body" in `put_object` operations. - """ - if isinstance(value, bytes) and len(value) > max_len: - return b"..." - - return value - - if not span.is_recording(): - return - - # Do not trace `Key Management Service` or `Secure Token Service` API calls - # over concerns of security leaks. - if aws_service not in {"kms", "sts"}: - tags = { - name: value - for (name, value) in zip(args_names, args) - if name in args_traced - } - tags = flatten_dict(tags) - - for param_key, value in tags.items(): - if param_key in SERVICE_PARAMS_BLOCK_LIST.get(aws_service, {}): - continue - - span.set_attribute(param_key, truncate_arg_value(value)) diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-boto/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py b/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py deleted file mode 100644 index 89684d4a05..0000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py +++ /dev/null @@ -1,285 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import skipUnless -from unittest.mock import Mock, patch - -import boto.awslambda -import boto.ec2 -import boto.elasticache -import boto.s3 -import boto.sts -from moto import ( # pylint: disable=import-error - mock_ec2_deprecated, - mock_lambda_deprecated, - mock_s3_deprecated, - mock_sts_deprecated, -) - -from opentelemetry.instrumentation.boto import BotoInstrumentor -from opentelemetry.semconv._incubating.attributes.http_attributes import ( - HTTP_METHOD, - HTTP_STATUS_CODE, -) -from opentelemetry.test.test_base import TestBase - - -def assert_span_http_status_code(span, code): - """Assert on the span's 'http.status_code' tag""" - tag = span.attributes[HTTP_STATUS_CODE] - assert tag == code, f"{tag} != {code}" - - -class TestBotoInstrumentor(TestBase): - """Botocore integration testsuite""" - - def setUp(self): - super().setUp() - BotoInstrumentor().instrument() - - def tearDown(self): - BotoInstrumentor().uninstrument() - - @mock_ec2_deprecated - def test_ec2_client(self): - ec2 = boto.ec2.connect_to_region("us-west-2") - - ec2.get_all_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual(span.attributes["aws.operation"], "DescribeInstances") - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes[HTTP_METHOD], "POST") - self.assertEqual(span.attributes["aws.region"], "us-west-2") - - # Create an instance - ec2.run_instances(21) - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 2) - span = spans[1] - self.assertEqual(span.attributes["aws.operation"], "RunInstances") - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes["endpoint"], "ec2") - self.assertEqual(span.attributes["http_method"], "runinstances") - self.assertEqual(span.attributes[HTTP_METHOD], "POST") - self.assertEqual(span.attributes["aws.region"], "us-west-2") - self.assertEqual(span.name, "ec2.command") - - @mock_ec2_deprecated - def test_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - with patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - ec2 = boto.ec2.connect_to_region("us-west-2") - ec2.get_all_instances() - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - @mock_ec2_deprecated - def test_analytics_enabled_with_rate(self): - ec2 = boto.ec2.connect_to_region("us-west-2") - - ec2.get_all_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - - @mock_ec2_deprecated - def test_analytics_enabled_without_rate(self): - ec2 = boto.ec2.connect_to_region("us-west-2") - - ec2.get_all_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - - @mock_s3_deprecated - def test_s3_client(self): - s3 = boto.s3.connect_to_region("us-east-1") - - s3.get_all_buckets() - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - span = spans[0] - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes[HTTP_METHOD], "GET") - self.assertEqual(span.attributes["aws.operation"], "get_all_buckets") - - # Create a bucket command - s3.create_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 2) - span = spans[1] - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes[HTTP_METHOD], "PUT") - self.assertEqual(span.attributes["path"], "/") - self.assertEqual(span.attributes["aws.operation"], "create_bucket") - - # Get the created bucket - s3.get_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 3) - span = spans[2] - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes["endpoint"], "s3") - self.assertEqual(span.attributes["http_method"], "head") - self.assertEqual(span.attributes[HTTP_METHOD], "HEAD") - self.assertEqual(span.attributes["aws.operation"], "head_bucket") - self.assertEqual(span.name, "s3.command") - - # Checking for resource in case of error - try: - s3.get_bucket("big_bucket") - except Exception: # pylint: disable=broad-except - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[2] - self.assertEqual(spans[2].attributes["endpoint"], "s3") - self.assertEqual(spans[2].attributes["http_method"], "head") - - @mock_s3_deprecated - def test_s3_put(self): - s3 = boto.s3.connect_to_region("us-east-1") - s3.create_bucket("mybucket") - bucket = s3.get_bucket("mybucket") - key = boto.s3.key.Key(bucket) - key.key = "foo" - key.set_contents_from_string("bar") - - spans = self.memory_exporter.get_finished_spans() - assert spans - # create bucket - self.assertEqual(len(spans), 3) - self.assertEqual(spans[0].attributes["aws.operation"], "create_bucket") - assert_span_http_status_code(spans[0], 200) - self.assertEqual(spans[0].attributes["endpoint"], "s3") - self.assertEqual(spans[0].attributes["http_method"], "put") - # get bucket - self.assertEqual(spans[1].attributes["aws.operation"], "head_bucket") - self.assertEqual(spans[1].attributes["endpoint"], "s3") - self.assertEqual(spans[1].attributes["http_method"], "head") - # put object - self.assertEqual( - spans[2].attributes["aws.operation"], "_send_file_internal" - ) - self.assertEqual(spans[2].attributes["endpoint"], "s3") - self.assertEqual(spans[2].attributes["http_method"], "put") - - @mock_lambda_deprecated - def test_unpatch(self): - lamb = boto.awslambda.connect_to_region("us-east-2") - - BotoInstrumentor().uninstrument() - - # multiple calls - lamb.list_functions() - spans = self.memory_exporter.get_finished_spans() - assert not spans, spans - - @mock_s3_deprecated - def test_double_patch(self): - s3 = boto.s3.connect_to_region("us-east-1") - - BotoInstrumentor().instrument() - BotoInstrumentor().instrument() - - # Get the created bucket - s3.create_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - - @mock_s3_deprecated - def test_uninstrument(self): - s3 = boto.s3.connect_to_region("us-east-1") - # Get the created bucket - s3.create_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - - self.memory_exporter.clear() - BotoInstrumentor().uninstrument() - - s3.get_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 0) - - @mock_lambda_deprecated - def test_lambda_client(self): - lamb = boto.awslambda.connect_to_region("us-east-2") - - # multiple calls - lamb.list_functions() - lamb.list_functions() - - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 2) - span = spans[0] - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes["endpoint"], "lambda") - self.assertEqual(span.attributes["http_method"], "get") - self.assertEqual(span.attributes[HTTP_METHOD], "GET") - self.assertEqual(span.attributes["aws.region"], "us-east-2") - self.assertEqual(span.attributes["aws.operation"], "list_functions") - - @mock_sts_deprecated - def test_sts_client(self): - sts = boto.sts.connect_to_region("us-west-2") - - sts.get_federation_token(12, duration=10) - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(span.attributes["endpoint"], "sts") - self.assertEqual(span.attributes["http_method"], "getfederationtoken") - self.assertEqual(span.attributes["aws.region"], "us-west-2") - self.assertEqual( - span.attributes["aws.operation"], "GetFederationToken" - ) - - # checking for protection on sts against security leak - self.assertTrue("args.path" not in span.attributes.keys()) - - @skipUnless( - False, - ( - "Test to reproduce the case where args sent to patched function " - "are None, can't be mocked: needs AWS credentials" - ), - ) - def test_elasticache_client(self): - elasticache = boto.elasticache.connect_to_region("us-west-2") - - elasticache.describe_cache_clusters() - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(span.attributes["endpoint"], "elasticcache") - self.assertEqual(span.attributes["aws.region"], "us-west-2") diff --git a/instrumentation/opentelemetry-instrumentation-boto3sqs/pyproject.toml b/instrumentation/opentelemetry-instrumentation-boto3sqs/pyproject.toml index 4c8d569003..d649734720 100644 --- a/instrumentation/opentelemetry-instrumentation-boto3sqs/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-boto3sqs/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Boto3 SQS service tracing for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/version.py b/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/version.py +++ b/instrumentation/opentelemetry-instrumentation-boto3sqs/src/opentelemetry/instrumentation/boto3sqs/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-boto3sqs/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-boto3sqs/test-requirements.txt index 5adcd8ad15..7d86ce381c 100644 --- a/instrumentation/opentelemetry-instrumentation-boto3sqs/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-boto3sqs/test-requirements.txt @@ -5,7 +5,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 jmespath==1.0.1 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 python-dateutil==2.8.2 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/README.rst b/instrumentation/opentelemetry-instrumentation-botocore/README.rst index 5b70922e07..61804f740b 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/README.rst +++ b/instrumentation/opentelemetry-instrumentation-botocore/README.rst @@ -6,7 +6,7 @@ OpenTelemetry Botocore Tracing .. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-botocore.svg :target: https://pypi.org/project/opentelemetry-instrumentation-botocore/ -This library allows tracing requests made by the Botocore library. +This library allows tracing requests made by the botocore and aiobotocore libraries. Extensions ---------- @@ -14,14 +14,14 @@ Extensions The instrumentation supports creating extensions for AWS services for enriching what is collected. We have extensions for the following AWS services: -- Bedrock Runtime +- Bedrock Runtime (botocore only) - DynamoDB - Lambda - SNS - SQS -Bedrock Runtime -*************** +Bedrock Runtime (botocore only) +******************************* This extension implements the GenAI semantic conventions for the following API calls: diff --git a/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml b/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml index 484e1a8cb2..5dd212a31b 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Botocore instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,18 +26,22 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.37", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", "opentelemetry-propagator-aws-xray ~= 1.0", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] -instruments = [ +instruments = [] +instruments-any = [ "botocore ~= 1.0", + "aiobotocore ~= 2.0", ] [project.entry-points.opentelemetry_instrumentor] botocore = "opentelemetry.instrumentation.botocore:BotocoreInstrumentor" +aiobotocore = "opentelemetry.instrumentation.botocore:AiobotocoreInstrumentor" [project.urls] Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-botocore" diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py index 6fd42d6ab4..fb9074d193 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py @@ -13,14 +13,15 @@ # limitations under the License. """ -Instrument `Botocore`_ to trace service requests. +Instrument `botocore`_ and `aiobotocore`_ to trace service requests. There are two options for instrumenting code. The first option is to use the ``opentelemetry-instrument`` executable which will automatically -instrument your Botocore client. The second is to programmatically enable +instrument your botocore or aiobotocore client. The second is to programmatically enable instrumentation via the following code: -.. _Botocore: https://pypi.org/project/botocore/ +.. _botocore: https://pypi.org/project/botocore/ +.. _aiobotocore: https://pypi.org/project/aiobotocore/ Usage ----- @@ -31,10 +32,10 @@ import botocore.session - # Instrument Botocore + # Instrument botocore BotocoreInstrumentor().instrument() - # This will create a span with Botocore-specific attributes + # This will create a span with botocore-specific attributes session = botocore.session.get_session() session.set_credentials( access_key="access-key", secret_key="secret-key" @@ -42,6 +43,27 @@ ec2 = session.create_client("ec2", region_name="us-west-2") ec2.describe_instances() +Async Usage +----------- + +.. code:: python + + from opentelemetry.instrumentation.botocore import AiobotocoreInstrumentor + import aiobotocore.session + import asyncio + + + async def main(): + # Instrument Aiobotocore + AiobotocoreInstrumentor().instrument() + + # This will create a span with aiobotocore-specific attributes + session = aiobotocore.session.get_session() + async with session.create_client("ec2") as client: + await client.describe_instances() + + asyncio.run(main()) + Thread Context Propagation -------------------------- @@ -63,7 +85,7 @@ API --- -The `instrument` method accepts the following keyword args: +The `instrument` method (for both ``BotocoreInstrumentor`` and ``AiobotocoreInstrumentor``) accepts the following keyword args: * tracer_provider (``TracerProvider``) - an optional tracer provider * request_hook (``Callable[[Span, str, str, dict], None]``) - a function with extra user-defined logic to be performed before performing the request @@ -84,10 +106,10 @@ def response_hook(span, service_name, operation_name, result): # response hook logic pass - # Instrument Botocore with hooks + # Instrument botocore with hooks BotocoreInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook) - # This will create a span with Botocore-specific attributes, including custom attributes added from the hooks + # This will create a span with botocore-specific attributes, including custom attributes added from the hooks session = botocore.session.get_session() session.set_credentials( access_key="access-key", secret_key="secret-key" @@ -97,34 +119,42 @@ def response_hook(span, service_name, operation_name, result): """ import logging -from typing import Any, Callable, Collection, Dict, Optional, Tuple +from typing import Any, Collection, Dict, Optional, Tuple from botocore.client import BaseClient from botocore.endpoint import Endpoint from botocore.exceptions import ClientError from wrapt import wrap_function_wrapper -from opentelemetry._logs import get_logger from opentelemetry.instrumentation.botocore.extensions import ( - _find_extension, - _has_extension, + _AIOBOTOCORE_EXTENSIONS, + _BOTOCORE_EXTENSIONS, +) +from opentelemetry.instrumentation.botocore.extensions.registry import ( + ExtensionRegistry, ) from opentelemetry.instrumentation.botocore.extensions.types import ( _AwsSdkCallContext, - _AwsSdkExtension, _BotocoreInstrumentorContext, ) -from opentelemetry.instrumentation.botocore.package import _instruments -from opentelemetry.instrumentation.botocore.utils import get_server_attributes -from opentelemetry.instrumentation.botocore.version import __version__ +from opentelemetry.instrumentation.botocore.package import ( + _instruments_aiobotocore, + _instruments_botocore, +) +from opentelemetry.instrumentation.botocore.utils import ( + _safe_invoke, + get_server_attributes, +) from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import ( is_instrumentation_enabled, suppress_http_instrumentation, unwrap, ) -from opentelemetry.metrics import Instrument, Meter, get_meter -from opentelemetry.propagators.aws.aws_xray_propagator import AwsXRayPropagator +from opentelemetry.propagators.aws.aws_xray_propagator import ( + TRACE_HEADER_KEY, + AwsXRayPropagator, +) from opentelemetry.semconv._incubating.attributes.cloud_attributes import ( CLOUD_REGION, ) @@ -136,7 +166,6 @@ def response_hook(span, service_name, operation_name, result): RPC_SERVICE, RPC_SYSTEM, ) -from opentelemetry.trace import get_tracer from opentelemetry.trace.span import Span logger = logging.getLogger(__name__) @@ -150,25 +179,20 @@ class BotocoreInstrumentor(BaseInstrumentor): def __init__(self): super().__init__() - self.request_hook = None - self.response_hook = None - self.propagator = AwsXRayPropagator() + if not hasattr(self, "request_hook"): + self.request_hook = None + if not hasattr(self, "response_hook"): + self.response_hook = None + if not hasattr(self, "extension_registry"): + self.extension_registry = None + if not hasattr(self, "propagator"): + self.propagator = AwsXRayPropagator() def instrumentation_dependencies(self) -> Collection[str]: - return _instruments + return _instruments_botocore def _instrument(self, **kwargs): # pylint: disable=attribute-defined-outside-init - - # tracers are lazy initialized per-extension in _get_tracer - self._tracers = {} - # loggers are lazy initialized per-extension in _get_logger - self._loggers = {} - # meters are lazy initialized per-extension in _get_meter - self._meters = {} - # metrics are lazy initialized per-extension in _get_metrics - self._metrics: Dict[str, Dict[str, Instrument]] = {} - self.request_hook = kwargs.get("request_hook") self.response_hook = kwargs.get("response_hook") @@ -176,9 +200,13 @@ def _instrument(self, **kwargs): if propagator is not None: self.propagator = propagator - self.tracer_provider = kwargs.get("tracer_provider") - self.logger_provider = kwargs.get("logger_provider") - self.meter_provider = kwargs.get("meter_provider") + self.extension_registry = ExtensionRegistry( + __name__, + _BOTOCORE_EXTENSIONS, + kwargs.get("tracer_provider"), + kwargs.get("logger_provider"), + kwargs.get("meter_provider"), + ) wrap_function_wrapper( "botocore.client", @@ -192,84 +220,168 @@ def _instrument(self, **kwargs): self._patched_endpoint_prepare_request, ) - @staticmethod - def _get_instrumentation_name(extension: _AwsSdkExtension) -> str: - has_extension = _has_extension(extension._call_context) - return ( - f"{__name__}.{extension._call_context.service}" - if has_extension - else __name__ + def _uninstrument(self, **kwargs): + unwrap(BaseClient, "_make_api_call") + unwrap(Endpoint, "prepare_request") + + # pylint: disable=unused-argument + def _patched_endpoint_prepare_request( + self, wrapped, instance, args, kwargs + ): + request = args[0] + headers = request.headers + + # There may be situations where both Botocore and Aiobotocore are + # instrumented at the same time. To avoid double-injection of headers, + # we add a check to see if the header is already present. If it is, + # we skip injection. + if TRACE_HEADER_KEY in headers: + return wrapped(*args, **kwargs) + + # Only the x-ray header is propagated by AWS services. Using any + # other propagator will lose the trace context. + self.propagator.inject(headers) + + return wrapped(*args, **kwargs) + + # pylint: disable=too-many-branches + def _patched_api_call(self, original_func, instance, args, kwargs): + if not is_instrumentation_enabled(): + return original_func(*args, **kwargs) + + call_context = _determine_call_context(instance, args) + if call_context is None: + return original_func(*args, **kwargs) + + extension = self.extension_registry.get_extension(call_context) + if not extension.should_trace_service_call(): + return original_func(*args, **kwargs) + + attributes = { + RPC_SYSTEM: "aws-api", + RPC_SERVICE: call_context.service_id, + RPC_METHOD: call_context.operation, + CLOUD_REGION: call_context.region, + **get_server_attributes(call_context.endpoint_url), + } + + _safe_invoke(extension.extract_attributes, attributes) + end_span_on_exit = extension.should_end_span_on_exit() + + tracer = self.extension_registry.get_tracer(extension) + metrics = self.extension_registry.get_metrics(extension) + instrumentor_ctx = _BotocoreInstrumentorContext( + logger=self.extension_registry.get_logger(extension), + metrics=metrics, ) + with tracer.start_as_current_span( + call_context.span_name, + kind=call_context.span_kind, + attributes=attributes, + # tracing streaming services require to close the span manually + # at a later time after the stream has been consumed + end_on_exit=end_span_on_exit, + ) as span: + _safe_invoke(extension.before_service_call, span, instrumentor_ctx) + self._call_request_hook(span, call_context) - def _get_tracer(self, extension: _AwsSdkExtension): - """This is a multiplexer in order to have a tracer per extension""" + try: + with suppress_http_instrumentation(): + result = None + try: + result = original_func(*args, **kwargs) + except ClientError as error: + result = getattr(error, "response", None) + _apply_response_attributes(span, result) + _safe_invoke( + extension.on_error, span, error, instrumentor_ctx + ) + raise + _apply_response_attributes(span, result) + _safe_invoke( + extension.on_success, span, result, instrumentor_ctx + ) + finally: + _safe_invoke(extension.after_service_call, instrumentor_ctx) + self._call_response_hook(span, call_context, result) - instrumentation_name = self._get_instrumentation_name(extension) - tracer = self._tracers.get(instrumentation_name) - if tracer: - return tracer + return result - schema_version = extension.tracer_schema_version() - self._tracers[instrumentation_name] = get_tracer( - instrumentation_name, - __version__, - self.tracer_provider, - schema_url=f"https://opentelemetry.io/schemas/{schema_version}", + def _call_request_hook(self, span: Span, call_context: _AwsSdkCallContext): + if not callable(self.request_hook): + return + self.request_hook( + span, + call_context.service, + call_context.operation, + call_context.params, ) - return self._tracers[instrumentation_name] - - def _get_logger(self, extension: _AwsSdkExtension): - """This is a multiplexer in order to have a logger per extension""" - - instrumentation_name = self._get_instrumentation_name(extension) - instrumentation_logger = self._loggers.get(instrumentation_name) - if instrumentation_logger: - return instrumentation_logger - - schema_version = extension.event_logger_schema_version() - self._loggers[instrumentation_name] = get_logger( - instrumentation_name, - "", - schema_url=f"https://opentelemetry.io/schemas/{schema_version}", - logger_provider=self.logger_provider, + + def _call_response_hook( + self, span: Span, call_context: _AwsSdkCallContext, result + ): + if not callable(self.response_hook): + return + self.response_hook( + span, call_context.service, call_context.operation, result ) - return self._loggers[instrumentation_name] - def _get_meter(self, extension: _AwsSdkExtension): - """This is a multiplexer in order to have a meter per extension""" +class AiobotocoreInstrumentor(BaseInstrumentor): + """An instrumentor for Aiobotocore. - instrumentation_name = self._get_instrumentation_name(extension) - meter = self._meters.get(instrumentation_name) - if meter: - return meter + See `BaseInstrumentor` + """ - schema_version = extension.meter_schema_version() - self._meters[instrumentation_name] = get_meter( - instrumentation_name, - "", - schema_url=f"https://opentelemetry.io/schemas/{schema_version}", - meter_provider=self.meter_provider, - ) + def __init__(self): + super().__init__() + if not hasattr(self, "request_hook"): + self.request_hook = None + if not hasattr(self, "response_hook"): + self.response_hook = None + if not hasattr(self, "extension_registry"): + self.extension_registry = None + if not hasattr(self, "propagator"): + self.propagator = AwsXRayPropagator() + + def instrumentation_dependencies(self) -> Collection[str]: + return _instruments_aiobotocore + + def _instrument(self, **kwargs): + # Verify that aiobotocore is present + # pylint: disable-next=import-outside-toplevel, unused-import + import aiobotocore.client # noqa: PLC0415, F401 + + # pylint: disable=attribute-defined-outside-init + self.request_hook = kwargs.get("request_hook") + self.response_hook = kwargs.get("response_hook") - return self._meters[instrumentation_name] + propagator = kwargs.get("propagator") + if propagator is not None: + self.propagator = propagator - def _get_metrics( - self, extension: _AwsSdkExtension, meter: Meter - ) -> Dict[str, Instrument]: - """This is a multiplexer for lazy initialization of metrics required by extensions""" - instrumentation_name = self._get_instrumentation_name(extension) - metrics = self._metrics.get(instrumentation_name) - if metrics is not None: - return metrics + self.extension_registry = ExtensionRegistry( + __name__, + _AIOBOTOCORE_EXTENSIONS, + kwargs.get("tracer_provider"), + kwargs.get("logger_provider"), + kwargs.get("meter_provider"), + ) - self._metrics.setdefault(instrumentation_name, {}) - metrics = self._metrics[instrumentation_name] - _safe_invoke(extension.setup_metrics, meter, metrics) - return metrics + wrap_function_wrapper( + "aiobotocore.client", + "AioBaseClient._make_api_call", + self._patched_api_call, + ) + + wrap_function_wrapper( + "botocore.endpoint", + "Endpoint.prepare_request", + self._patched_endpoint_prepare_request, + ) def _uninstrument(self, **kwargs): - unwrap(BaseClient, "_make_api_call") + unwrap("aiobotocore.client.AioBaseClient", "_make_api_call") unwrap(Endpoint, "prepare_request") # pylint: disable=unused-argument @@ -279,6 +391,13 @@ def _patched_endpoint_prepare_request( request = args[0] headers = request.headers + # There may be situations where both Botocore and Aiobotocore are + # instrumented at the same time. To avoid double-injection of headers, + # we add a check to see if the header is already present. If it is, + # we skip injection. + if TRACE_HEADER_KEY in headers: + return wrapped(*args, **kwargs) + # Only the x-ray header is propagated by AWS services. Using any # other propagator will lose the trace context. self.propagator.inject(headers) @@ -286,17 +405,17 @@ def _patched_endpoint_prepare_request( return wrapped(*args, **kwargs) # pylint: disable=too-many-branches - def _patched_api_call(self, original_func, instance, args, kwargs): + async def _patched_api_call(self, original_func, instance, args, kwargs): if not is_instrumentation_enabled(): - return original_func(*args, **kwargs) + return await original_func(*args, **kwargs) call_context = _determine_call_context(instance, args) if call_context is None: - return original_func(*args, **kwargs) + return await original_func(*args, **kwargs) - extension = _find_extension(call_context) + extension = self.extension_registry.get_extension(call_context) if not extension.should_trace_service_call(): - return original_func(*args, **kwargs) + return await original_func(*args, **kwargs) attributes = { RPC_SYSTEM: "aws-api", @@ -309,11 +428,10 @@ def _patched_api_call(self, original_func, instance, args, kwargs): _safe_invoke(extension.extract_attributes, attributes) end_span_on_exit = extension.should_end_span_on_exit() - tracer = self._get_tracer(extension) - meter = self._get_meter(extension) - metrics = self._get_metrics(extension, meter) + tracer = self.extension_registry.get_tracer(extension) + metrics = self.extension_registry.get_metrics(extension) instrumentor_ctx = _BotocoreInstrumentorContext( - logger=self._get_logger(extension), + logger=self.extension_registry.get_logger(extension), metrics=metrics, ) with tracer.start_as_current_span( @@ -331,7 +449,7 @@ def _patched_api_call(self, original_func, instance, args, kwargs): with suppress_http_instrumentation(): result = None try: - result = original_func(*args, **kwargs) + result = await original_func(*args, **kwargs) except ClientError as error: result = getattr(error, "response", None) _apply_response_attributes(span, result) @@ -418,14 +536,3 @@ def _determine_call_context( # extracting essential attributes ('service' and 'operation') failed. logger.error("Error when initializing call context", exc_info=ex) return None - - -def _safe_invoke(function: Callable, *args): - function_name = "" - try: - function_name = function.__name__ - function(*args) - except Exception as ex: # pylint:disable=broad-except - logger.error( - "Error when invoking function '%s'", function_name, exc_info=ex - ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py index dd8ba24e9f..feffa34098 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/__init__.py @@ -15,11 +15,6 @@ import importlib import logging -from opentelemetry.instrumentation.botocore.extensions.types import ( - _AwsSdkCallContext, - _AwsSdkExtension, -) - _logger = logging.getLogger(__name__) @@ -31,7 +26,7 @@ def loader(): return loader -_KNOWN_EXTENSIONS = { +_BOTOCORE_EXTENSIONS = { "bedrock-runtime": _lazy_load(".bedrock", "_BedrockRuntimeExtension"), "dynamodb": _lazy_load(".dynamodb", "_DynamoDbExtension"), "lambda": _lazy_load(".lmbd", "_LambdaExtension"), @@ -43,19 +38,14 @@ def loader(): "sqs": _lazy_load(".sqs", "_SqsExtension"), } - -def _has_extension(call_context: _AwsSdkCallContext) -> bool: - return call_context.service in _KNOWN_EXTENSIONS - - -def _find_extension(call_context: _AwsSdkCallContext) -> _AwsSdkExtension: - try: - loader = _KNOWN_EXTENSIONS.get(call_context.service) - if loader is None: - return _AwsSdkExtension(call_context) - - extension_cls = loader() - return extension_cls(call_context) - except Exception as ex: # pylint: disable=broad-except - _logger.error("Error when loading extension: %s", ex) - return _AwsSdkExtension(call_context) +_AIOBOTOCORE_EXTENSIONS = { + # TODO: Add Bedrock support for aiobotocore + "dynamodb": _lazy_load(".dynamodb", "_DynamoDbExtension"), + "lambda": _lazy_load(".lmbd", "_LambdaExtension"), + "secretsmanager": _lazy_load( + ".secretsmanager", "_SecretsManagerExtension" + ), + "stepfunctions": _lazy_load(".sfns", "_StepFunctionsExtension"), + "sns": _lazy_load(".sns", "_SnsExtension"), + "sqs": _lazy_load(".sqs", "_SqsExtension"), +} diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py index 0b164b1f92..930b436942 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/bedrock_utils.py @@ -20,7 +20,12 @@ from typing import Any, Callable, Dict, Iterator, Sequence, Union from botocore.eventstream import EventStream, EventStreamError -from wrapt import ObjectProxy + +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy from opentelemetry._logs import LogRecord from opentelemetry.context import get_current @@ -37,7 +42,7 @@ # pylint: disable=abstract-method -class ConverseStreamWrapper(ObjectProxy): +class ConverseStreamWrapper(BaseObjectProxy): """Wrapper for botocore.eventstream.EventStream""" def __init__( @@ -61,7 +66,7 @@ def __init__( def __iter__(self): try: - for event in self.__wrapped__: + for event in self.__wrapped__: # pylint: disable=no-member self._process_event(event) yield event except EventStreamError as exc: @@ -143,7 +148,7 @@ def _process_event(self, event): return def close(self): - self.__wrapped__.close() + self.__wrapped__.close() # pylint: disable=no-member # Treat the stream as done to ensure the span end. self._complete_stream(self._response) @@ -157,7 +162,7 @@ def _handle_stream_error(self, exc): # pylint: disable=abstract-method -class InvokeModelWithResponseStreamWrapper(ObjectProxy): +class InvokeModelWithResponseStreamWrapper(BaseObjectProxy): """Wrapper for botocore.eventstream.EventStream""" def __init__( @@ -183,7 +188,7 @@ def __init__( self._ended = False def close(self): - self.__wrapped__.close() + self.__wrapped__.close() # pylint: disable=no-member # Treat the stream as done to ensure the span end. self._stream_done_callback(self._response, self._ended) @@ -197,7 +202,7 @@ def _handle_stream_error(self, exc): def __iter__(self): try: - for event in self.__wrapped__: + for event in self.__wrapped__: # pylint: disable=no-member self._process_event(event) yield event except EventStreamError as exc: diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/registry.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/registry.py new file mode 100644 index 0000000000..5f285dd311 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/registry.py @@ -0,0 +1,216 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING, Callable, Mapping, Optional + +from opentelemetry._logs import get_logger +from opentelemetry.instrumentation.botocore.extensions.types import ( + _AwsSdkCallContext, + _AwsSdkExtension, +) +from opentelemetry.instrumentation.botocore.utils import _safe_invoke +from opentelemetry.instrumentation.botocore.version import __version__ +from opentelemetry.metrics import get_meter +from opentelemetry.trace import get_tracer + +if TYPE_CHECKING: + from opentelemetry._logs import Logger, LoggerProvider + from opentelemetry.metrics import Instrument, Meter, MeterProvider + from opentelemetry.trace import Tracer, TracerProvider + + +_logger = logging.getLogger(__name__) + + +class ExtensionRegistry: + """ + Registry for AWS SDK extensions that manages extension lookup and + associated OpenTelemetry instrumentation components (tracers, loggers, meters, metrics). + """ + + def __init__( + self, + package_name: str, + extensions: Mapping[str, Callable[[], type[_AwsSdkExtension]]], + tracer_provider: Optional[TracerProvider] = None, + logger_provider: Optional[LoggerProvider] = None, + meter_provider: Optional[MeterProvider] = None, + ): + self._package_name = package_name + self._extensions: Mapping[ + str, Callable[[], type[_AwsSdkExtension]] + ] = extensions + self._tracer_provider: TracerProvider = tracer_provider + self._logger_provider: LoggerProvider = logger_provider + self._meter_provider: MeterProvider = meter_provider + self._tracers: dict[str, Tracer] = {} + self._loggers: dict[str, Logger] = {} + self._meters: dict[str, Meter] = {} + self._metrics: dict[str, dict[str, Instrument]] = {} + + def get_extension( + self, call_context: _AwsSdkCallContext + ) -> _AwsSdkExtension: + """ + Get the appropriate extension for a given call context. + + Args: + call_context: The AWS SDK call context + + Returns: + The matching extension for the service/operation + """ + try: + loader: Callable[[], type[_AwsSdkExtension]] = ( + self._extensions.get(call_context.service) + ) + if loader is None: + return _AwsSdkExtension(call_context) + extension_cls = loader() + return extension_cls(call_context) + except Exception as exc: # pylint: disable=broad-except + _logger.error("Error when loading extension: %s", exc) + return _AwsSdkExtension(call_context) + + def has_extension(self, call_context: _AwsSdkCallContext) -> bool: + """ + Check if a dedicated extension exists for the given call context. + + Args: + call_context: The AWS SDK call context + + Returns: + True if a service-specific extension exists, False otherwise + """ + return call_context.service in self._extensions + + def get_instrumentation_name(self, extension: _AwsSdkExtension) -> str: + """ + Get the instrumentation name for an extension. + + Service-specific extensions get a namespaced name (e.g., 'module.s3'), + while the default extension uses just the module name. + + Args: + extension: The AWS SDK extension + + Returns: + The instrumentation name string + """ + if self.has_extension(extension._call_context): + return f"{self._package_name}.{extension._call_context.service}" + return self._package_name + + def get_tracer(self, extension: _AwsSdkExtension) -> Tracer: + """ + Get or create a tracer for the given extension. + + Tracers are cached per instrumentation name for reuse. + + Args: + extension: The AWS SDK extension + + Returns: + A configured Tracer instance + """ + instrumentation_name: str = self.get_instrumentation_name(extension) + if instrumentation_name in self._tracers: + return self._tracers[instrumentation_name] + + schema_version: str = extension.tracer_schema_version() + tracer: Tracer = get_tracer( + instrumentation_name, + __version__, + self._tracer_provider, + schema_url=f"https://opentelemetry.io/schemas/{schema_version}", + ) + self._tracers[instrumentation_name] = tracer + return tracer + + def get_logger(self, extension: _AwsSdkExtension): + """ + Get or create a logger for the given extension. + + Loggers are cached per instrumentation name for reuse. + + Args: + extension: The AWS SDK extension + + Returns: + A configured Logger instance + """ + instrumentation_name: str = self.get_instrumentation_name(extension) + if instrumentation_name in self._loggers: + return self._loggers[instrumentation_name] + + schema_version: str = extension.event_logger_schema_version() + logger: Logger = get_logger( + instrumentation_name, + schema_url=f"https://opentelemetry.io/schemas/{schema_version}", + logger_provider=self._logger_provider, + ) + self._loggers[instrumentation_name] = logger + return logger + + def get_meter(self, extension: _AwsSdkExtension) -> Meter: + """ + Get or create a meter for the given extension. + + Meters are cached per instrumentation name for reuse. + + Args: + extension: The AWS SDK extension + + Returns: + A configured Meter instance + """ + instrumentation_name: str = self.get_instrumentation_name(extension) + if instrumentation_name in self._meters: + return self._meters[instrumentation_name] + + schema_version: str = extension.meter_schema_version() + meter = get_meter( + instrumentation_name, + "", + schema_url=f"https://opentelemetry.io/schemas/{schema_version}", + meter_provider=self._meter_provider, + ) + self._meters[instrumentation_name] = meter + return meter + + def get_metrics( + self, extension: _AwsSdkExtension + ) -> dict[str, Instrument]: + """ + Get or create metrics for the given extension. + + Metrics are lazily initialized by calling the extension's setup_metrics method. + + Args: + extension: The AWS SDK extension + + Returns: + A dictionary mapping metric names to Instrument instances + """ + instrumentation_name: str = self.get_instrumentation_name(extension) + if instrumentation_name in self._metrics: + return self._metrics[instrumentation_name] + + meter: Meter = self.get_meter(extension) + metrics: dict[str, Instrument] = {} + _safe_invoke(extension.setup_metrics, meter, metrics) + self._metrics[instrumentation_name] = metrics + return metrics diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/package.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/package.py index a06b9c206b..13b44a7b7d 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/package.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/package.py @@ -12,5 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +_instruments_botocore = ("botocore~=1.0",) +_instruments_aiobotocore = ("aiobotocore~=2.0",) -_instruments = ("botocore ~= 1.0",) +_instruments = () +_instruments_any = (*_instruments_botocore, *_instruments_aiobotocore) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/utils.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/utils.py index 4309e6e9bd..29e8d12b88 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/utils.py @@ -13,6 +13,8 @@ # limitations under the License. from __future__ import annotations +import logging +from typing import Callable from urllib.parse import urlparse from opentelemetry.semconv._incubating.attributes import ( @@ -20,6 +22,8 @@ ) from opentelemetry.util.types import AttributeValue +_logger = logging.getLogger(__name__) + def get_server_attributes(endpoint_url: str) -> dict[str, AttributeValue]: """Extract server.* attributes from AWS endpoint URL.""" @@ -29,3 +33,14 @@ def get_server_attributes(endpoint_url: str) -> dict[str, AttributeValue]: attributes[ServerAttributes.SERVER_ADDRESS] = parsed.hostname attributes[ServerAttributes.SERVER_PORT] = parsed.port or 443 return attributes + + +def _safe_invoke(function: Callable, *args): + function_name = "" + try: + function_name = function.__name__ + function(*args) + except Exception as ex: # pylint:disable=broad-except + _logger.error( + "Error when invoking function '%s'", function_name, exc_info=ex + ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/version.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/version.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt1.txt new file mode 100644 index 0000000000..57149ca01e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt2.txt new file mode 100644 index 0000000000..7ac7981294 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0.txt index f6243241bd..ba323fede9 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0.txt @@ -6,7 +6,6 @@ certifi==2024.7.4 cffi==1.17.0 charset-normalizer==3.3.2 cryptography==43.0.1 -Deprecated==1.2.14 docker==7.0.0 idna==3.7 iniconfig==2.0.0 @@ -15,7 +14,7 @@ jmespath==1.0.1 MarkupSafe==2.1.5 moto==5.1.11 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pycparser==2.21 pytest==7.4.4 @@ -31,7 +30,6 @@ tomli==2.0.1 typing_extensions==4.12.2 urllib3==1.26.19 Werkzeug==3.0.6 -wrapt==1.16.0 xmltodict==0.13.0 zipp==3.19.2 -e opentelemetry-instrumentation diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt1.txt new file mode 100644 index 0000000000..eb6190d00d --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt2.txt new file mode 100644 index 0000000000..eaac680e2e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1.txt index 5c7cb24a0c..361b6ce1bb 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1.txt @@ -6,7 +6,6 @@ certifi==2024.7.4 cffi==1.17.0 charset-normalizer==3.3.2 cryptography==43.0.1 -Deprecated==1.2.14 docker==7.0.0 idna==3.7 iniconfig==2.0.0 @@ -15,7 +14,7 @@ jmespath==1.0.1 MarkupSafe==2.1.5 moto==5.1.11 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pycparser==2.21 pytest==7.4.4 @@ -31,7 +30,6 @@ tomli==2.0.1 typing_extensions==4.12.2 urllib3==1.26.19 Werkzeug==3.0.6 -wrapt==1.16.0 xmltodict==0.13.0 zipp==3.19.2 -e opentelemetry-instrumentation diff --git a/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-2.txt new file mode 100644 index 0000000000..69dc82bdfa --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-2.txt @@ -0,0 +1,40 @@ +asgiref==3.8.1 +aws-xray-sdk==2.12.1 +boto3==1.29.4 +botocore==1.32.4 +aiobotocore==2.8.0 +certifi==2024.7.4 +cffi==1.17.0 +charset-normalizer==3.3.2 +cryptography==43.0.1 +Deprecated==1.2.14 +docker==7.0.0 +idna==3.7 +iniconfig==2.0.0 +Jinja2==3.1.6 +jmespath==1.0.1 +MarkupSafe==2.1.5 +moto==5.1.11 +packaging==24.0 +pluggy==1.5.0 +py-cpuinfo==9.0.0 +pycparser==2.21 +pytest==7.4.4 +pytest-vcr==1.0.2 +python-dateutil==2.8.2 +pytz==2024.1 +PyYAML==6.0.1 +requests==2.32.3 +responses==0.25.0 +s3transfer==0.7.0 +six==1.16.0 +tomli==2.0.1 +typing_extensions==4.12.2 +urllib3==1.26.19 +Werkzeug==3.0.6 +wrapt==1.16.0 +xmltodict==0.13.0 +zipp==3.19.2 +-e opentelemetry-instrumentation +-e propagator/opentelemetry-propagator-aws-xray +-e instrumentation/opentelemetry-instrumentation-botocore diff --git a/instrumentation/opentelemetry-instrumentation-boto/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-3.txt similarity index 69% rename from instrumentation/opentelemetry-instrumentation-boto/test-requirements.txt rename to instrumentation/opentelemetry-instrumentation-botocore/test-requirements-3.txt index d73e7447ed..518da691cc 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-3.txt @@ -1,7 +1,8 @@ asgiref==3.8.1 -boto==2.49.0 -boto3==1.34.44 -botocore==1.34.44 +aws-xray-sdk==2.12.1 +boto3==1.35.16 +botocore==1.35.16 +aiobotocore==2.15.0 certifi==2024.7.4 cffi==1.17.0 charset-normalizer==3.3.2 @@ -13,12 +14,13 @@ iniconfig==2.0.0 Jinja2==3.1.6 jmespath==1.0.1 MarkupSafe==2.1.5 -moto==2.3.2 +moto==5.1.11 packaging==24.0 pluggy==1.5.0 py-cpuinfo==9.0.0 pycparser==2.21 pytest==7.4.4 +pytest-vcr==1.0.2 python-dateutil==2.8.2 pytz==2024.1 PyYAML==6.0.1 @@ -29,9 +31,10 @@ six==1.16.0 tomli==2.0.1 typing_extensions==4.12.2 urllib3==1.26.19 -Werkzeug==2.3.8 +Werkzeug==3.0.6 wrapt==1.16.0 xmltodict==0.13.0 zipp==3.19.2 -e opentelemetry-instrumentation --e instrumentation/opentelemetry-instrumentation-boto +-e propagator/opentelemetry-propagator-aws-xray +-e instrumentation/opentelemetry-instrumentation-botocore diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_aiobotocore_instrumentation.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_aiobotocore_instrumentation.py new file mode 100644 index 0000000000..216f2e29be --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_aiobotocore_instrumentation.py @@ -0,0 +1,460 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint:disable=too-many-public-methods +import asyncio +import importlib.util +import json +import unittest +from typing import Any +from unittest.mock import Mock, call, patch + +import botocore.stub +from botocore.exceptions import ClientError + +from opentelemetry import trace as trace_api +from opentelemetry.instrumentation.auto_instrumentation import ( + _load_instrumentors, +) +from opentelemetry.instrumentation.botocore import AiobotocoreInstrumentor +from opentelemetry.instrumentation.utils import suppress_instrumentation +from opentelemetry.semconv._incubating.attributes.cloud_attributes import ( + CLOUD_REGION, +) +from opentelemetry.semconv._incubating.attributes.http_attributes import ( + HTTP_STATUS_CODE, +) +from opentelemetry.semconv._incubating.attributes.rpc_attributes import ( + RPC_METHOD, + RPC_SERVICE, + RPC_SYSTEM, +) +from opentelemetry.semconv.attributes.exception_attributes import ( + EXCEPTION_MESSAGE, + EXCEPTION_STACKTRACE, + EXCEPTION_TYPE, +) +from opentelemetry.semconv.attributes.server_attributes import ( + SERVER_ADDRESS, + SERVER_PORT, +) +from opentelemetry.test.test_base import TestBase + +_REQUEST_ID_REGEX_MATCH = r"[A-Za-z0-9]{52}" + +aiobotocore_installed = importlib.util.find_spec("aiobotocore") is not None + + +@unittest.skipIf(not aiobotocore_installed, "aiobotocore is not installed") +class TestAiobotocoreInstrumentor(TestBase): + """Aiobotocore integration testsuite""" + + def setUp(self): + # pylint: disable-next=import-outside-toplevel + import aiobotocore.session # noqa: PLC0415 + + super().setUp() + AiobotocoreInstrumentor().instrument() + self.session = aiobotocore.session.get_session() + self.session.set_credentials( + access_key="access-key", + secret_key="secret-key", + ) + self.region = "us-west-2" + + def tearDown(self): + super().tearDown() + AiobotocoreInstrumentor().uninstrument() + + def _default_span_attributes(self, service: str, operation: str): + return { + RPC_SYSTEM: "aws-api", + RPC_SERVICE: service, + RPC_METHOD: operation, + CLOUD_REGION: self.region, + "retry_attempts": 0, + HTTP_STATUS_CODE: 200, + SERVER_ADDRESS: f"{service.lower()}.{self.region}.amazonaws.com", + SERVER_PORT: 443, + } + + def assert_only_span(self): + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(1, len(spans)) + return spans[0] + + def assert_span( + self, + service: str, + operation: str, + request_id=None, + attributes=None, + ): + span = self.assert_only_span() + expected = self._default_span_attributes(service, operation) + if attributes: + expected.update(attributes) + + span_attributes_request_id = "aws.request_id" + if request_id is _REQUEST_ID_REGEX_MATCH: + actual_request_id = span.attributes[span_attributes_request_id] + self.assertRegex(actual_request_id, _REQUEST_ID_REGEX_MATCH) + expected[span_attributes_request_id] = actual_request_id + elif request_id is not None: + expected[span_attributes_request_id] = request_id + + self.assertSpanHasAttributes(span, expected) + self.assertEqual(f"{service}.{operation}", span.name) + return span + + def _make_client(self, service: str): + return self.session.create_client(service, region_name=self.region) + + @staticmethod + def _make_response_meta(request_id: str) -> dict[str, Any]: + return { + "RequestId": request_id, + "RetryAttempts": 0, + "HTTPStatusCode": 200, + } + + def test_traced_client_ec2(self): + """Test basic EC2 client tracing with stubbed response.""" + request_id = "fdcdcab1-ae5c-489e-9c33-4637c5dda355" + + async def _test(): + async with self._make_client("ec2") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "describe_instances", + { + "Reservations": [], + "ResponseMetadata": self._make_response_meta( + request_id + ), + }, + ) + await client.describe_instances() + + asyncio.run(_test()) + self.assert_span("EC2", "DescribeInstances", request_id=request_id) + + def test_traced_client_s3(self): + """Test S3 client tracing with stubbed response.""" + request_id = "s3-request-id-12345" + + async def _test(): + async with self._make_client("s3") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "list_buckets", + { + "Buckets": [], + "Owner": {"ID": "owner-id"}, + "ResponseMetadata": self._make_response_meta( + request_id + ), + }, + ) + await client.list_buckets() + + asyncio.run(_test()) + self.assert_span("S3", "ListBuckets", request_id=request_id) + + def test_no_op_tracer_provider(self): + """Test that no spans are created when using NoOpTracerProvider.""" + AiobotocoreInstrumentor().uninstrument() + AiobotocoreInstrumentor().instrument( + tracer_provider=trace_api.NoOpTracerProvider() + ) + + async def _test(): + async with self._make_client("ec2") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "describe_instances", + { + "Reservations": [], + "ResponseMetadata": self._make_response_meta( + "test-id" + ), + }, + ) + await client.describe_instances() + + asyncio.run(_test()) + spans_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans_list), 0) + + def test_not_recording(self): + """Test behavior when span is not recording.""" + mock_tracer = Mock() + mock_span = Mock() + mock_span.is_recording.return_value = False + mock_tracer.start_span.return_value = mock_span + + async def _test(): + with patch("opentelemetry.trace.get_tracer") as tracer: + tracer.return_value = mock_tracer + async with self._make_client("ec2") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "describe_instances", + { + "Reservations": [], + "ResponseMetadata": self._make_response_meta( + "test-id" + ), + }, + ) + await client.describe_instances() + self.assertFalse(mock_span.is_recording()) + self.assertTrue(mock_span.is_recording.called) + self.assertFalse(mock_span.set_attribute.called) + self.assertFalse(mock_span.set_status.called) + + asyncio.run(_test()) + + def test_client_error(self): + """Test that ClientError is properly traced with error status.""" + + async def _test(): + async with self._make_client("s3") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_client_error( + "get_object", + service_error_code="NoSuchKey", + service_message="The specified key does not exist.", + http_status_code=404, + ) + with self.assertRaises(ClientError): + await client.get_object( + Bucket="test-bucket", Key="test-key" + ) + + asyncio.run(_test()) + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(1, len(spans)) + span = spans[0] + + self.assertIs(span.status.status_code, trace_api.StatusCode.ERROR) + self.assertEqual("S3.GetObject", span.name) + + # Verify exception event was recorded + self.assertEqual(1, len(span.events)) + event = span.events[0] + self.assertIn(EXCEPTION_STACKTRACE, event.attributes) + self.assertIn(EXCEPTION_TYPE, event.attributes) + self.assertIn(EXCEPTION_MESSAGE, event.attributes) + + def test_suppress_instrumentation(self): + """Test that instrumentation can be suppressed.""" + + async def _test(): + async with self._make_client("ec2") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "describe_instances", + { + "Reservations": [], + "ResponseMetadata": self._make_response_meta( + "test-id" + ), + }, + ) + stubber.add_response( + "describe_instances", + { + "Reservations": [], + "ResponseMetadata": self._make_response_meta( + "test-id-2" + ), + }, + ) + with suppress_instrumentation(): + await client.describe_instances() + await client.describe_instances() + + asyncio.run(_test()) + self.assertEqual(0, len(self.get_finished_spans())) + + def test_request_hook(self): + """Test that request hook is called with correct parameters.""" + request_hook_service_attribute_name = "request_hook.service_name" + request_hook_operation_attribute_name = "request_hook.operation_name" + request_hook_api_params_attribute_name = "request_hook.api_params" + + def request_hook(span, service_name, operation_name, api_params): + hook_attributes = { + request_hook_service_attribute_name: service_name, + request_hook_operation_attribute_name: operation_name, + request_hook_api_params_attribute_name: json.dumps(api_params), + } + span.set_attributes(hook_attributes) + + AiobotocoreInstrumentor().uninstrument() + AiobotocoreInstrumentor().instrument(request_hook=request_hook) + + request_id = "hook-test-request-id" + + async def _test(): + async with self._make_client("s3") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "list_objects_v2", + { + "Contents": [], + "ResponseMetadata": self._make_response_meta( + request_id + ), + }, + expected_params={"Bucket": "test-bucket"}, + ) + await client.list_objects_v2(Bucket="test-bucket") + + asyncio.run(_test()) + + self.assert_span( + "S3", + "ListObjectsV2", + request_id=request_id, + attributes={ + request_hook_service_attribute_name: "s3", + request_hook_operation_attribute_name: "ListObjectsV2", + request_hook_api_params_attribute_name: json.dumps( + {"Bucket": "test-bucket"} + ), + }, + ) + + def test_response_hook(self): + """Test that response hook is called with correct parameters.""" + response_hook_service_attribute_name = "response_hook.service_name" + response_hook_operation_attribute_name = "response_hook.operation_name" + response_hook_bucket_count_attribute_name = ( + "response_hook.bucket_count" + ) + + def response_hook(span, service_name, operation_name, result): + hook_attributes = { + response_hook_service_attribute_name: service_name, + response_hook_operation_attribute_name: operation_name, + response_hook_bucket_count_attribute_name: len( + result["Buckets"] + ), + } + span.set_attributes(hook_attributes) + + AiobotocoreInstrumentor().uninstrument() + AiobotocoreInstrumentor().instrument(response_hook=response_hook) + + request_id = "response-hook-test-id" + + async def _test(): + async with self._make_client("s3") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "list_buckets", + { + "Buckets": [ + {"Name": "bucket1"}, + {"Name": "bucket2"}, + ], + "Owner": {"ID": "owner-id"}, + "ResponseMetadata": self._make_response_meta( + request_id + ), + }, + ) + await client.list_buckets() + + asyncio.run(_test()) + + self.assert_span( + "S3", + "ListBuckets", + request_id=request_id, + attributes={ + response_hook_service_attribute_name: "s3", + response_hook_operation_attribute_name: "ListBuckets", + response_hook_bucket_count_attribute_name: 2, + }, + ) + + def test_multiple_operations(self): + """Test tracing multiple sequential operations.""" + + async def _test(): + async with self._make_client("s3") as client: + with botocore.stub.Stubber(client) as stubber: + stubber.add_response( + "list_buckets", + { + "Buckets": [], + "Owner": {"ID": "owner-id"}, + "ResponseMetadata": self._make_response_meta( + "req-1" + ), + }, + ) + stubber.add_response( + "list_buckets", + { + "Buckets": [], + "Owner": {"ID": "owner-id"}, + "ResponseMetadata": self._make_response_meta( + "req-2" + ), + }, + ) + await client.list_buckets() + await client.list_buckets() + + asyncio.run(_test()) + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(2, len(spans)) + + for span in spans: + self.assertEqual("S3.ListBuckets", span.name) + + self.assertEqual("req-1", spans[0].attributes["aws.request_id"]) + self.assertEqual("req-2", spans[1].attributes["aws.request_id"]) + + @patch( + "opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts" + ) + @patch("opentelemetry.instrumentation.auto_instrumentation._load._logger") + def test_instruments_with_aiobotocore_installed( + self, mock_logger, mock_dep + ): + mock_distro = Mock() + mock_dep.return_value = None + mock_distro.load_instrumentor.return_value = None + _load_instrumentors(mock_distro) + self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 2) + eps = [ + c[0][0].name for c in mock_distro.load_instrumentor.call_args_list + ] + assert "aiobotocore" in eps + assert "botocore" in eps + + mock_logger.debug.assert_has_calls( + [ + call("Instrumented %s", "aiobotocore"), + call("Instrumented %s", "botocore"), + ], + any_order=True, + ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py index a8a0afe112..bf8a2ac242 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_bedrock.py @@ -1485,7 +1485,11 @@ def test_invoke_model_with_content( "content": "\n\nA man stands before a crowd of people", } finish_reason = "length" + else: + pytest.xfail("model family not handled: {model_family}") + assert_message_in_logs(logs[0], "gen_ai.user.message", user_content, span) + choice_body = { "index": 0, "finish_reason": finish_reason, @@ -1621,6 +1625,8 @@ def test_invoke_model_with_content_different_events( system = anthropic_claude_system() finish_reason = "end_turn" choice_content = [{"type": "text", "text": "This is a test"}] + else: + pytest.xfail("llm_model_value not handled: {llm_model_value}") body = get_invoke_model_body( llm_model_value, @@ -2060,6 +2066,8 @@ def test_invoke_model_with_content_tool_call( elif model_family == "anthropic.claude": llm_model_value = "us.anthropic.claude-3-5-sonnet-20240620-v1:0" llm_model_config = AnthropicClaudeModel + else: + pytest.xfail("model family not handled: {model_family}") invoke_model_tool_call( span_exporter, @@ -2143,6 +2151,9 @@ def test_invoke_model_no_content( elif model_family == "mistral.mistral": choice_message = {} finish_reason = "length" + else: + pytest.xfail("model family not handled: {model_family}") + choice_body = { "index": 0, "finish_reason": finish_reason, @@ -2173,6 +2184,8 @@ def test_invoke_model_no_content_different_events( messages = anthropic_claude_messages() system = anthropic_claude_system() finish_reason = "end_turn" + else: + pytest.xfail("llm_model_value not handled: {llm_model_value}") body = get_invoke_model_body( llm_model_value, @@ -2226,6 +2239,8 @@ def test_invoke_model_no_content_tool_call( elif model_family == "anthropic.claude": llm_model_value = "us.anthropic.claude-3-5-sonnet-20240620-v1:0" llm_model_config = AnthropicClaudeModel + else: + pytest.xfail("model family not handled: {model_family}") invoke_model_tool_call( span_exporter, @@ -2378,6 +2393,9 @@ def test_invoke_model_with_response_stream_with_content( {"text": "\nHello! I am a computer program designed to"} ] } + else: + pytest.xfail("model family not handled: {model_family}") + choice_body = { "index": 0, "finish_reason": finish_reason, @@ -2410,6 +2428,8 @@ def test_invoke_model_with_response_stream_with_content_different_events( system = anthropic_claude_system() finish_reason = "end_turn" choice_content = [{"text": "This is a test", "type": "text"}] + else: + pytest.xfail("llm_model_value not handled: {llm_model_value}") max_tokens = 10 body = get_invoke_model_body( @@ -2642,6 +2662,8 @@ def test_invoke_model_with_response_stream_with_content_tool_call( elif model_family == "anthropic.claude": llm_model_value = "us.anthropic.claude-3-5-sonnet-20240620-v1:0" llm_model_config = AnthropicClaudeModel + else: + pytest.xfail("model family not handled: {model_family}") invoke_model_with_response_stream_tool_call( span_exporter, @@ -2768,6 +2790,8 @@ def test_invoke_model_with_response_stream_no_content_different_events( messages = anthropic_claude_messages() system = anthropic_claude_system() finish_reason = "end_turn" + else: + pytest.xfail("llm_model_value not handled: {llm_model_value}") max_tokens = 10 body = get_invoke_model_body( @@ -2828,6 +2852,8 @@ def test_invoke_model_with_response_stream_no_content_tool_call( elif model_family == "anthropic.claude": llm_model_value = "us.anthropic.claude-3-5-sonnet-20240620-v1:0" llm_model_config = AnthropicClaudeModel + else: + pytest.xfail("model family not handled: {model_family}") invoke_model_with_response_stream_tool_call( span_exporter, diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py index 4cc259a860..e5efa882ee 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py @@ -13,13 +13,17 @@ # limitations under the License. import json import os -from unittest.mock import ANY, Mock, patch +from importlib.metadata import EntryPoint +from unittest.mock import ANY, Mock, call, patch import botocore.session from botocore.exceptions import ParamValidationError from moto import mock_aws # pylint: disable=import-error from opentelemetry import trace as trace_api +from opentelemetry.instrumentation.auto_instrumentation import ( + _load_instrumentors, +) from opentelemetry.instrumentation.botocore import BotocoreInstrumentor from opentelemetry.instrumentation.utils import ( suppress_http_instrumentation, @@ -568,3 +572,33 @@ def test_server_attributes_with_custom_endpoint(self): SERVER_PORT: 2025, }, ) + + @patch( + "opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts" + ) + @patch("opentelemetry.instrumentation.auto_instrumentation._load._logger") + def test_instruments_with_botocore_installed(self, mock_logger, mock_dep): + def _load_instrumentor(ep: EntryPoint, **kwargs): + # simulate aiobotocore not being present + if ep.name == "aiobotocore": + raise ModuleNotFoundError("aiobotocore") + + mock_distro = Mock() + mock_dep.return_value = None + mock_distro.load_instrumentor.side_effect = _load_instrumentor + _load_instrumentors(mock_distro) + eps = [ + c[0][0].name for c in mock_distro.load_instrumentor.call_args_list + ] + self.assertIn("botocore", eps) + mock_logger.debug.assert_has_calls( + [ + call("Instrumented %s", "botocore"), + call( + "Skipping instrumentation %s: %s", + "aiobotocore", + "aiobotocore", + ), + ], + any_order=True, + ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_lambda.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_lambda.py index 3ff8ca7f22..4c3295c45d 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_lambda.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_lambda.py @@ -113,7 +113,7 @@ def _create_lambda_function(self, function_name: str, function_code: str): self.client.create_function( FunctionName=function_name, - Runtime="python3.9", + Runtime="python3.10", Role=role_arn, Handler="lambda_function.lambda_handler", Code={ diff --git a/instrumentation/opentelemetry-instrumentation-cassandra/pyproject.toml b/instrumentation/opentelemetry-instrumentation-cassandra/pyproject.toml index 4c7c92203f..6b51889b3c 100644 --- a/instrumentation/opentelemetry-instrumentation-cassandra/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-cassandra/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Cassandra instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-cassandra/src/opentelemetry/instrumentation/cassandra/version.py b/instrumentation/opentelemetry-instrumentation-cassandra/src/opentelemetry/instrumentation/cassandra/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-cassandra/src/opentelemetry/instrumentation/cassandra/version.py +++ b/instrumentation/opentelemetry-instrumentation-cassandra/src/opentelemetry/instrumentation/cassandra/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements-scylla.txt b/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements-scylla.txt index af661fb9b8..1411ba97fd 100644 --- a/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements-scylla.txt +++ b/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements-scylla.txt @@ -5,7 +5,7 @@ Deprecated==1.2.14 geomet==0.2.1.post1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 python-snappy==0.7.3 diff --git a/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt index b2e996f840..1cdec8d3cd 100644 --- a/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt @@ -6,7 +6,7 @@ Deprecated==1.2.14 geomet==0.2.1.post1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 PyYAML==6.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-celery/pyproject.toml b/instrumentation/opentelemetry-instrumentation-celery/pyproject.toml index 7a105fa439..21a58ea35e 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-celery/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Celery Instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py index 8bd10a0a58..3d6691f382 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py @@ -182,7 +182,7 @@ def _trace_prerun(self, *args, **kwargs): ) activation = trace.use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call utils.attach_context(task, task_id, span, activation, token) def _trace_postrun(self, *args, **kwargs): @@ -247,7 +247,7 @@ def _trace_before_publish(self, *args, **kwargs): utils.set_attributes_from_context(span, kwargs) activation = trace.use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call utils.attach_context( task, task_id, span, activation, None, is_publish=True @@ -274,7 +274,7 @@ def _trace_after_publish(*args, **kwargs): _, activation, _ = ctx - activation.__exit__(None, None, None) # pylint: disable=E1101 + activation.__exit__(None, None, None) # pylint: disable=unnecessary-dunder-call utils.detach_context(task, task_id, is_publish=True) @staticmethod diff --git a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/version.py b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/version.py +++ b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-celery/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-celery/test-requirements-0.txt index 811d805642..b5ee53c0a9 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-celery/test-requirements-0.txt @@ -11,7 +11,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 kombu==5.3.5 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 prompt-toolkit==3.0.43 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt index 4d3afc74e4..f57dd4b27b 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt @@ -10,7 +10,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 kombu==5.3.5 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 prompt-toolkit==3.0.43 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-click/pyproject.toml b/instrumentation/opentelemetry-instrumentation-click/pyproject.toml index 6d7c502547..2fbcdf19c0 100644 --- a/instrumentation/opentelemetry-instrumentation-click/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-click/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Click instrumentation for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-click/src/opentelemetry/instrumentation/click/version.py b/instrumentation/opentelemetry-instrumentation-click/src/opentelemetry/instrumentation/click/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-click/src/opentelemetry/instrumentation/click/version.py +++ b/instrumentation/opentelemetry-instrumentation-click/src/opentelemetry/instrumentation/click/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-click/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-click/test-requirements.txt index f4d715a5e1..a210b9cf55 100644 --- a/instrumentation/opentelemetry-instrumentation-click/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-click/test-requirements.txt @@ -8,7 +8,7 @@ itsdangerous==2.1.2 Jinja2==3.1.6 MarkupSafe==2.1.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytest-asyncio==0.23.5 diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/pyproject.toml b/instrumentation/opentelemetry-instrumentation-confluent-kafka/pyproject.toml index d28eb98593..6958b515ad 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Confluent Kafka instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,15 +25,13 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-instrumentation == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", "opentelemetry-api ~= 1.12", - "wrapt >= 1.0.0, < 2.0.0", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] -instruments = [ - "confluent-kafka >= 1.8.2, <= 2.13.0", -] +instruments = ["confluent-kafka >= 1.8.2, < 3.0.0"] [project.entry-points.opentelemetry_instrumentor] confluent_kafka = "opentelemetry.instrumentation.confluent_kafka:ConfluentKafkaInstrumentor" @@ -47,10 +44,7 @@ Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib" path = "src/opentelemetry/instrumentation/confluent_kafka/version.py" [tool.hatch.build.targets.sdist] -include = [ - "/src", - "/tests", -] +include = ["/src", "/tests"] [tool.hatch.build.targets.wheel] packages = ["src/opentelemetry"] diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py index f4606bc4d9..ed390d7006 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py @@ -383,11 +383,11 @@ def wrap_poll(func, instance, tracer, args, kwargs): if instance._current_consume_span: _end_current_consume_span(instance) - with tracer.start_as_current_span( - "recv", end_on_exit=True, kind=trace.SpanKind.CONSUMER - ): - record = func(*args, **kwargs) - if record: + record = func(*args, **kwargs) + if record: + with tracer.start_as_current_span( + "recv", end_on_exit=True, kind=trace.SpanKind.CONSUMER + ): _create_new_consume_span(instance, tracer, [record]) _enrich_span( instance._current_consume_span, @@ -396,9 +396,9 @@ def wrap_poll(func, instance, tracer, args, kwargs): record.offset(), operation=MessagingOperationTypeValues.PROCESS, ) - instance._current_context_token = context.attach( - trace.set_span_in_context(instance._current_consume_span) - ) + instance._current_context_token = context.attach( + trace.set_span_in_context(instance._current_consume_span) + ) return record @@ -407,21 +407,20 @@ def wrap_consume(func, instance, tracer, args, kwargs): if instance._current_consume_span: _end_current_consume_span(instance) - with tracer.start_as_current_span( - "recv", end_on_exit=True, kind=trace.SpanKind.CONSUMER - ): - records = func(*args, **kwargs) - if len(records) > 0: + records = func(*args, **kwargs) + if len(records) > 0: + with tracer.start_as_current_span( + "recv", end_on_exit=True, kind=trace.SpanKind.CONSUMER + ): _create_new_consume_span(instance, tracer, records) _enrich_span( instance._current_consume_span, records[0].topic(), operation=MessagingOperationTypeValues.PROCESS, ) - - instance._current_context_token = context.attach( - trace.set_span_in_context(instance._current_consume_span) - ) + instance._current_context_token = context.attach( + trace.set_span_in_context(instance._current_consume_span) + ) return records diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/package.py b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/package.py index e7aa44893a..23aca3b92d 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/package.py +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/package.py @@ -13,4 +13,4 @@ # limitations under the License. -_instruments = ("confluent-kafka >= 1.8.2, <= 2.13.0",) +_instruments = ("confluent-kafka >= 1.8.2, < 3.0.0",) diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/version.py b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/version.py +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-confluent-kafka/test-requirements.txt index 3e9b88bbdb..283b8525ad 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/test-requirements.txt @@ -1,9 +1,9 @@ asgiref==3.8.1 -confluent-kafka==2.13.0 +confluent-kafka==2.13.2 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py index 365ac333d9..772ecd09ee 100644 --- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py @@ -197,7 +197,6 @@ def test_poll(self) -> None: MESSAGING_MESSAGE_ID: "topic-30.1.3", }, }, - {"name": "recv", "attributes": {}}, ] consumer = MockConsumer( @@ -213,7 +212,7 @@ def test_poll(self) -> None: consumer.poll() consumer.poll() consumer.poll() - consumer.poll() + consumer.poll() # empty poll — must not produce a span span_list = self.memory_exporter.get_finished_spans() self._compare_spans(span_list, expected_spans) @@ -259,7 +258,6 @@ def test_consume(self) -> None: SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value, }, }, - {"name": "recv", "attributes": {}}, ] consumer = MockConsumer( @@ -276,10 +274,86 @@ def test_consume(self) -> None: consumer.consume(3) consumer.consume(1) consumer.consume(2) - consumer.consume(1) + consumer.consume(1) # empty consume — must not produce a span span_list = self.memory_exporter.get_finished_spans() self._compare_spans(span_list, expected_spans) + def test_poll_empty_does_not_create_span(self) -> None: + instrumentation = ConfluentKafkaInstrumentor() + consumer = MockConsumer( + [], + { + "bootstrap.servers": "localhost:29092", + "group.id": "mygroup", + "auto.offset.reset": "earliest", + }, + ) + self.memory_exporter.clear() + consumer = instrumentation.instrument_consumer(consumer) + consumer.poll() + consumer.poll() + + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 0) + + def test_consume_empty_does_not_create_span(self) -> None: + instrumentation = ConfluentKafkaInstrumentor() + consumer = MockConsumer( + [], + { + "bootstrap.servers": "localhost:29092", + "group.id": "mygroup", + "auto.offset.reset": "earliest", + }, + ) + self.memory_exporter.clear() + consumer = instrumentation.instrument_consumer(consumer) + consumer.consume(5) + consumer.consume(5) + + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 0) + + def test_poll_empty_cleans_up_previous_span_and_token(self) -> None: + instrumentation = ConfluentKafkaInstrumentor() + consumer = MockConsumer( + [MockedMessage("topic-1", 0, 0, [])], + { + "bootstrap.servers": "localhost:29092", + "group.id": "mygroup", + "auto.offset.reset": "earliest", + }, + ) + consumer = instrumentation.instrument_consumer(consumer) + consumer.poll() # non-empty: sets _current_consume_span and _current_context_token + self.assertIsNotNone(consumer._current_consume_span) + self.assertIsNotNone(consumer._current_context_token) + + consumer.poll() # empty: should clean up both + self.assertIsNone(consumer._current_consume_span) + self.assertIsNone(consumer._current_context_token) + + def test_consume_empty_cleans_up_previous_span_and_token(self) -> None: + instrumentation = ConfluentKafkaInstrumentor() + consumer = MockConsumer( + [MockedMessage("topic-1", 0, 0, [])], + { + "bootstrap.servers": "localhost:29092", + "group.id": "mygroup", + "auto.offset.reset": "earliest", + }, + ) + consumer = instrumentation.instrument_consumer(consumer) + consumer.consume( + 1 + ) # non-empty: sets _current_consume_span and _current_context_token + self.assertIsNotNone(consumer._current_consume_span) + self.assertIsNotNone(consumer._current_context_token) + + consumer.consume(1) # empty: should clean up both + self.assertIsNone(consumer._current_consume_span) + self.assertIsNone(consumer._current_context_token) + def test_close(self) -> None: instrumentation = ConfluentKafkaInstrumentor() mocked_messages = [ diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/pyproject.toml b/instrumentation/opentelemetry-instrumentation-dbapi/pyproject.toml index ade1be720e..225f9cf97b 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-dbapi/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Database API instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py index cd154616a6..9eb386b0ea 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py @@ -173,9 +173,14 @@ import re from typing import Any, Awaitable, Callable, Generic, TypeVar -import wrapt from wrapt import wrap_function_wrapper +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy + from opentelemetry import trace as trace_api from opentelemetry.instrumentation.dbapi.version import __version__ from opentelemetry.instrumentation.sqlcommenter_utils import _add_sql_comment @@ -372,7 +377,7 @@ def instrument_connection( Returns: An instrumented connection. """ - if isinstance(connection, wrapt.ObjectProxy): + if isinstance(connection, BaseObjectProxy): _logger.warning("Connection already instrumented") return connection @@ -407,7 +412,7 @@ def uninstrument_connection( Returns: An uninstrumented connection. """ - if isinstance(connection, wrapt.ObjectProxy): + if isinstance(connection, BaseObjectProxy): return connection.__wrapped__ _logger.warning("Connection is not instrumented") @@ -565,15 +570,15 @@ def get_connection_attributes(self, connection: object) -> None: self.span_attributes[NET_PEER_PORT] = port -# pylint: disable=abstract-method -class TracedConnectionProxy(wrapt.ObjectProxy, Generic[ConnectionT]): +# pylint: disable=abstract-method,no-member +class TracedConnectionProxy(BaseObjectProxy, Generic[ConnectionT]): # pylint: disable=unused-argument def __init__( self, connection: ConnectionT, db_api_integration: DatabaseApiIntegration | None = None, ): - wrapt.ObjectProxy.__init__(self, connection) + BaseObjectProxy.__init__(self, connection) self._self_db_api_integration = db_api_integration def __getattribute__(self, name: str): @@ -799,15 +804,15 @@ async def traced_execution_async( return await query_method(*args, **kwargs) -# pylint: disable=abstract-method -class TracedCursorProxy(wrapt.ObjectProxy, Generic[CursorT]): +# pylint: disable=abstract-method,no-member +class TracedCursorProxy(BaseObjectProxy, Generic[CursorT]): # pylint: disable=unused-argument def __init__( self, cursor: CursorT, db_api_integration: DatabaseApiIntegration, ): - wrapt.ObjectProxy.__init__(self, cursor) + BaseObjectProxy.__init__(self, cursor) self._self_cursor_tracer = CursorTracer[CursorT](db_api_integration) def execute(self, *args: Any, **kwargs: Any): diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/version.py b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/version.py index 46b492f255..d21c60d918 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/version.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/version.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" _instruments = tuple() diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt1.txt new file mode 100644 index 0000000000..ae191f8ee5 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt2.txt new file mode 100644 index 0000000000..01c75bb94f --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements.txt index a05fd86be5..fa7a4f9580 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements.txt @@ -1,13 +1,11 @@ asgiref==3.8.1 -Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-dbapi diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py index f9fb9c952b..2b6ed0e478 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py @@ -1052,7 +1052,7 @@ def test_wrap_connect(self, mock_dbapi): dbapi.wrap_connect(self.tracer, mock_dbapi, "connect", "-") connection = mock_dbapi.connect() self.assertEqual(mock_dbapi.connect.call_count, 1) - self.assertIsInstance(connection.__wrapped__, mock.Mock) + self.assertIsInstance(connection.__wrapped__, mock.Mock) # pylint: disable=no-member @mock.patch("opentelemetry.instrumentation.dbapi") def test_unwrap_connect(self, mock_dbapi): @@ -1071,7 +1071,7 @@ def test_instrument_connection(self): connection2 = dbapi.instrument_connection( "instrumenting_module_test_name", mocked_conn, "dbname" ) - self.assertIs(connection2.__wrapped__, mocked_conn) + self.assertIs(connection2.__wrapped__, mocked_conn) # pylint: disable=no-member @mock.patch("opentelemetry.instrumentation.dbapi.DatabaseApiIntegration") def test_instrument_connection_kwargs_defaults(self, mock_dbapiint): @@ -1138,7 +1138,7 @@ def test_uninstrument_connection(self): connection2 = dbapi.instrument_connection( "instrumenting_module_test_name", mocked_conn, "-" ) - self.assertIs(connection2.__wrapped__, mocked_conn) + self.assertIs(connection2.__wrapped__, mocked_conn) # pylint: disable=no-member connection3 = dbapi.uninstrument_connection(connection2) self.assertIs(connection3, mocked_conn) diff --git a/instrumentation/opentelemetry-instrumentation-django/pyproject.toml b/instrumentation/opentelemetry-instrumentation-django/pyproject.toml index 35df0c42de..56c624d3d5 100644 --- a/instrumentation/opentelemetry-instrumentation-django/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-django/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Instrumentation for Django" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,15 +26,15 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-wsgi == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-wsgi == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] asgi = [ - "opentelemetry-instrumentation-asgi == 0.62b0.dev", + "opentelemetry-instrumentation-asgi == 0.63b0.dev", ] instruments = [ "django >= 2.0", diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py index 5f142b614d..7194d7958f 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware/otel_middleware.py @@ -271,7 +271,7 @@ def process_request(self, request): span.set_attribute(key, value) activation = use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call request_start_time = default_timer() request.META[self._environ_timer_key] = request_start_time request.META[self._environ_activation_key] = activation diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/version.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/version.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt index f2375911d3..0ad78d4516 100644 --- a/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 Django==2.2.28 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytz==2024.1 @@ -12,6 +12,7 @@ tomli==2.0.1 typing_extensions==4.12.2 wrapt==1.16.0 zipp==3.19.2 +setuptools==79.0.1 ; python_version >= "3.12" -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-wsgi -e util/opentelemetry-util-http diff --git a/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt index 12f934acd4..042598256c 100644 --- a/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 Django==3.2.25 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytz==2024.1 diff --git a/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt index 6d84c3db89..40c6756d83 100644 --- a/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt @@ -1,10 +1,9 @@ asgiref==3.8.1 -backports.zoneinfo==0.2.1 Deprecated==1.2.14 Django==4.2.17 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 sqlparse==0.5.0 diff --git a/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt b/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt index e7c56b6050..f917be9715 100644 --- a/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt +++ b/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 Django==5.2.11 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 sqlparse==0.5.0 diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml b/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml index 9780e16b10..c7b962ae5a 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry elasticsearch instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/version.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/version.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt index 7507078f84..e74be56246 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-0.txt @@ -4,7 +4,7 @@ elasticsearch==6.8.2 elasticsearch-dsl==6.4.0 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 python-dateutil==2.8.2 diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt index fcd68f1c8e..1398647ba7 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-1.txt @@ -4,7 +4,7 @@ elasticsearch==7.17.9 elasticsearch-dsl==7.4.1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 python-dateutil==2.8.2 diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt index 05af6f4d6c..05446a82b6 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt @@ -5,7 +5,7 @@ elasticsearch-dsl==8.13.1 elastic-transport==8.13.0 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 python-dateutil==2.8.2 diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py index d8386a2f4e..1ec5ded749 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py @@ -42,6 +42,7 @@ major_version, minor_version = elasticsearch.VERSION[:2] +helpers = None if major_version == 8: from . import helpers_es8 as helpers # pylint: disable=no-name-in-module elif major_version == 7: diff --git a/instrumentation/opentelemetry-instrumentation-falcon/pyproject.toml b/instrumentation/opentelemetry-instrumentation-falcon/pyproject.toml index 460e50ba85..5eb789a5c7 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-falcon/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Falcon instrumentation for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-wsgi == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-wsgi == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", "packaging >= 20.0", ] diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 09429f8bb5..2da0f9964f 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -346,7 +346,7 @@ def _handle_exception(self, *args): return super()._handle_exception(*args) def __call__(self, env, start_response): - # pylint: disable=E1101 + # pylint: disable=unnecessary-dunder-call # pylint: disable=too-many-locals # pylint: disable=too-many-branches if self._otel_excluded_urls.url_disabled(env.get("PATH_INFO", "/")): diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/version.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/version.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-0.txt index 7b0ce1caf9..d0d776451e 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-0.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 falcon==1.4.1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 python-mimeparse==1.6.0 diff --git a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-1.txt index 51f09a4c46..22b0e6236c 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-1.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 falcon==2.0.0 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt index cff1244cd0..db9c60a0de 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-2.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 falcon==3.1.1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-3.txt b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-3.txt index 392a1ce385..f8f238be20 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-3.txt +++ b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-3.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 falcon==3.1.3 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-4.txt b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-4.txt index 0fd061b29a..d1c7700cd8 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-4.txt +++ b/instrumentation/opentelemetry-instrumentation-falcon/test-requirements-4.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 falcon==4.0.2 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/pyproject.toml b/instrumentation/opentelemetry-instrumentation-fastapi/pyproject.toml index ca9450f6db..c76d0f351c 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-fastapi/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry FastAPI Instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-asgi == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-asgi == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/version.py b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/version.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-fastapi/test-requirements.txt index 6c67a9f7ca..09e4a15c5e 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-fastapi/test-requirements.txt @@ -1,18 +1,18 @@ annotated-types==0.7.0 -anyio==4.3.0 +anyio==4.12.1 asgiref==3.8.1 certifi==2024.7.4 charset-normalizer==3.3.2 Deprecated==1.2.14 exceptiongroup==1.2.0 -fastapi==0.117.0 +fastapi==0.126.0 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pydantic==2.12.5 pydantic_core==2.41.5 diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py index 21275366e2..aa6189a60e 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py @@ -25,6 +25,7 @@ import fastapi import pytest +from fastapi.middleware.asyncexitstack import AsyncExitStackMiddleware from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware from fastapi.responses import JSONResponse, PlainTextResponse from fastapi.routing import APIRoute @@ -237,27 +238,27 @@ def _create_fastapi_app(): custom_router = fastapi.APIRouter(route_class=CustomRoute) @sub_app.get("/home") - async def _(): + async def _home(): return {"message": "sub hi"} @app.get("/foobar") - async def _(): + async def _foobar(): return {"message": "hello world"} @app.get("/user/{username}") - async def _(username: str): + async def _user(username: str): return {"message": username} @app.get("/exclude/{param}") - async def _(param: str): + async def _exclude(param: str): return {"message": param} @app.get("/healthzz") - async def _(): + async def _health(): return {"message": "ok"} @app.get("/error") - async def _(): + async def _error(): raise UnhandledException("This is an unhandled exception") @custom_router.get("/success") @@ -1021,27 +1022,27 @@ def _create_fastapi_app(): custom_router = fastapi.APIRouter(route_class=CustomRoute) @sub_app.get("/home") - async def _(): + async def _home(): return {"message": "sub hi"} @app.get("/foobar") - async def _(): + async def _foobar(): return {"message": "hello world"} @app.get("/user/{username}") - async def _(username: str): + async def _user(username: str): return {"message": username} @app.get("/exclude/{param}") - async def _(param: str): + async def _exclude(param: str): return {"message": param} @app.get("/healthzz") - async def _(): + async def _health(): return {"message": "ok"} @app.get("/error") - async def _(): + async def _error(): raise UnhandledException("This is an unhandled exception") @custom_router.get("/success") @@ -1964,7 +1965,7 @@ async def _(*_): return PlainTextResponse("", status_code) @self.app.get("/foobar") - async def _(): + async def _foobar(): self.request_trace_id = ( trace.get_current_span().get_span_context().trace_id ) @@ -2031,7 +2032,7 @@ def test_middleware_exceptions(self): """Exceptions from user middlewares are recorded in the active span""" @self.app.get("/foobar") - async def _(): + async def _foobar(): return PlainTextResponse("Hello World") @self.app.middleware("http") @@ -2073,7 +2074,12 @@ def _create_fastapi_app(): app = TestBaseFastAPI._create_fastapi_app() def build_middleware_stack(): - return app.router + # Return something that is NOT a ServerErrorMiddleware so the + # instrumentation fallback path triggers, but still wrap the + # router with AsyncExitStackMiddleware so that newer FastAPI + # versions (which assert ``fastapi_middleware_astack`` exists in + # the request scope) can service requests normally. + return AsyncExitStackMiddleware(app.router) app.build_middleware_stack = build_middleware_stack return app @@ -2098,7 +2104,7 @@ def test_no_instrumentation(self): self.assertEqual(len(errors), 1) self.assertEqual( errors[0].getMessage(), - "Skipping FastAPI instrumentation due to unexpected middleware stack: expected ServerErrorMiddleware, got ", + "Skipping FastAPI instrumentation due to unexpected middleware stack: expected ServerErrorMiddleware, got ", ) diff --git a/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml b/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml index ff7d3b60e7..4b43789e6c 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-flask/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Flask instrumentation for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-wsgi == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-wsgi == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", "packaging >= 21.0", ] diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py index c66e8fa10d..ced6c8c6f7 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py @@ -254,7 +254,6 @@ def response_hook(span: Span, status: str, response_headers: List): --- """ -import sys import weakref from logging import getLogger from time import time_ns @@ -518,7 +517,7 @@ def _before_request(): span.set_attributes(custom_attributes) activation = trace.use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call flask_request_environ[_ENVIRON_ACTIVATION_KEY] = activation flask_request_environ[_ENVIRON_REQCTX_REF_KEY] = _request_ctx_ref() flask_request_environ[_ENVIRON_SPAN_KEY] = span @@ -555,7 +554,7 @@ def _wrapped_teardown_request( excluded_urls=None, ): def _teardown_request(exc): - # pylint: disable=E1101 + # pylint: disable=unnecessary-dunder-call if excluded_urls and excluded_urls.url_disabled(flask.request.url): return @@ -582,14 +581,8 @@ def _teardown_request(exc): try: # For Flask 3.1+, check if this is a streaming response that might # have already been cleaned up to prevent double cleanup - # Only check for streaming in Flask 3.1+ and Python 3.10+ to avoid interference with older versions - is_flask_31_plus = _IS_FLASK_31_PLUS and sys.version_info >= ( - 3, - 10, - ) - is_streaming = False - if is_flask_31_plus: + if _IS_FLASK_31_PLUS: try: # Additional safety check: verify we're in a Flask request context if hasattr(flask, "request") and hasattr( @@ -605,7 +598,7 @@ def _teardown_request(exc): # Not in a proper Flask request context, don't check for streaming is_streaming = False - if is_flask_31_plus and is_streaming: + if _IS_FLASK_31_PLUS and is_streaming: # For Flask 3.1+ streaming responses, ensure OpenTelemetry contexts are cleaned up # This addresses the generator context leak issues documented by Logfire # (open-telemetry/opentelemetry-python#2606) diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/version.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/version.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-0.txt index 6bebbe414a..f2c46ec39f 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-0.txt @@ -7,7 +7,7 @@ itsdangerous==2.1.2 Jinja2==3.1.6 MarkupSafe==2.1.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-1.txt index c66c55df1d..14b208cc98 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-1.txt @@ -7,7 +7,7 @@ itsdangerous==2.1.2 Jinja2==3.1.6 MarkupSafe==2.1.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt index 3736417419..f891cae043 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-2.txt @@ -8,7 +8,7 @@ itsdangerous==2.1.2 Jinja2==3.1.6 MarkupSafe==2.1.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-3.txt b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-3.txt index 0658a90bc8..e4c5a1e04b 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/test-requirements-3.txt +++ b/instrumentation/opentelemetry-instrumentation-flask/test-requirements-3.txt @@ -8,7 +8,7 @@ itsdangerous>=2.2.0 Jinja2==3.1.6 MarkupSafe==2.1.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_flask_compatibility.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_flask_compatibility.py index dcf9b2bdd6..8e1804038b 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_flask_compatibility.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_flask_compatibility.py @@ -18,7 +18,6 @@ """ import io -import sys import threading import time from unittest import mock, skipIf @@ -297,10 +296,6 @@ def generate(): self.assertEqual(response.status_code, 200) self.assertEqual(response.data, b"Data") - @skipIf( - sys.version_info < (3, 10), - "Flask 3.1+ streaming context cleanup only enabled on Python 3.10+", - ) @skipIf( lambda: ( not __import__( diff --git a/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml b/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml index 9d6160919a..17df13ef8e 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-grpc/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry gRPC instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_server.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_server.py index 73a7c16c08..8ee022f33a 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_server.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_server.py @@ -14,7 +14,12 @@ import grpc import grpc.aio -import wrapt + +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy from opentelemetry.semconv._incubating.attributes.rpc_attributes import ( RPC_GRPC_STATUS_CODE, @@ -24,8 +29,8 @@ from ._utilities import _server_status -# pylint:disable=abstract-method -class _OpenTelemetryAioServicerContext(wrapt.ObjectProxy): +# pylint:disable=abstract-method,no-member +class _OpenTelemetryAioServicerContext(BaseObjectProxy): def __init__(self, servicer_context, active_span): super().__init__(servicer_context) self._self_active_span = active_span diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py index bbeb4ec1d9..a9264b56f4 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py @@ -60,6 +60,12 @@ def callback(response_future): code = response_future.code() if code != grpc.StatusCode.OK: rpc_info.error = code + span.set_attribute(RPC_GRPC_STATUS_CODE, code.value[0]) + span.set_status( + Status( + status_code=StatusCode.ERROR, + ) + ) return response = response_future.result() rpc_info.response = response @@ -228,7 +234,7 @@ def intercept_stream( if self._filter is not None and not self._filter(client_info): return invoker(request_or_iterator, metadata) - if client_info.is_server_stream: + if client_info.is_server_stream and not client_info.is_client_stream: return self._intercept_server_stream( request_or_iterator, metadata, client_info, invoker ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/version.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/version.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt1.txt new file mode 100644 index 0000000000..57149ca01e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt2.txt new file mode 100644 index 0000000000..7ac7981294 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0.txt index 3e688b45c2..d255ca9a24 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0.txt @@ -1,15 +1,13 @@ asgiref==3.8.1 -Deprecated==1.2.14 grpcio==1.62.0 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 protobuf==3.20.3 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-grpc diff --git a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt1.txt new file mode 100644 index 0000000000..eb6190d00d --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt2.txt new file mode 100644 index 0000000000..eaac680e2e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1.txt index 48def42151..a639d0346b 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1.txt @@ -1,15 +1,13 @@ asgiref==3.8.1 -Deprecated==1.2.14 grpcio==1.75.1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 protobuf==3.20.3 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-grpc diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py index 850ad79661..daa4d68dc3 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_aio_client_interceptor.py @@ -224,6 +224,10 @@ async def test_error_simple(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) async def test_error_unary_stream(self): with self.assertRaises(grpc.RpcError): @@ -237,6 +241,10 @@ async def test_error_unary_stream(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) async def test_error_stream_unary(self): with self.assertRaises(grpc.RpcError): @@ -249,6 +257,10 @@ async def test_error_stream_unary(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) async def test_error_stream_stream(self): with self.assertRaises(grpc.RpcError): @@ -264,6 +276,10 @@ async def test_error_stream_stream(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) # pylint:disable=no-self-use async def test_client_interceptor_trace_context_propagation(self): diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py index e001d2ed57..3830bf00c4 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py @@ -221,6 +221,51 @@ def test_stream_stream(self): }, ) + def test_stream_stream_preserves_call_interface(self): + """Regression test for issue #1180. + + Bidirectional streaming RPCs must return an object that implements + grpc.Call (add_done_callback, cancel, is_active, etc.) rather than + a bare generator. Before the fix, bidi streams were routed through + the generator-based _intercept_server_stream, which stripped the + grpc.Call interface and caused downstream code to crash with: + AttributeError: 'generator' object has no attribute 'add_done_callback' + """ + + def request_messages(): + for _ in range(5): + yield Request(client_id=1, request_data="data") + + response_iterator = self._stub.BidirectionalStreamingMethod( + request_messages(), metadata=(("key", "value"),) + ) + + for attr in ("add_done_callback", "cancel", "is_active"): + self.assertTrue( + hasattr(response_iterator, attr), + f"bidi stream response missing grpc.Call method '{attr}'", + ) + + list(response_iterator) + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + span = spans[0] + + self.assertEqual( + span.name, "/GRPCTestServer/BidirectionalStreamingMethod" + ) + self.assertIs(span.kind, trace.SpanKind.CLIENT) + self.assertSpanHasAttributes( + span, + { + RPC_METHOD: "BidirectionalStreamingMethod", + RPC_SERVICE: "GRPCTestServer", + RPC_SYSTEM: "grpc", + RPC_GRPC_STATUS_CODE: grpc.StatusCode.OK.value[0], + }, + ) + def test_error_simple(self): with self.assertRaises(grpc.RpcError): simple_method(self._stub, error=True) @@ -232,6 +277,10 @@ def test_error_simple(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) def test_error_stream_unary(self): with self.assertRaises(grpc.RpcError): @@ -244,6 +293,10 @@ def test_error_stream_unary(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) def test_error_unary_stream(self): with self.assertRaises(grpc.RpcError): @@ -256,6 +309,10 @@ def test_error_unary_stream(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) def test_error_stream_stream(self): with self.assertRaises(grpc.RpcError): @@ -268,6 +325,10 @@ def test_error_stream_stream(self): span.status.status_code, trace.StatusCode.ERROR, ) + self.assertEqual( + span.attributes[RPC_GRPC_STATUS_CODE], + grpc.StatusCode.INVALID_ARGUMENT.value[0], + ) def test_client_interceptor_falsy_response( self, diff --git a/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml b/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml index 2d09960f4c..f07cca6ca4 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-httpx/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry HTTPX Instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/version.py b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/version.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt1.txt new file mode 100644 index 0000000000..57149ca01e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt2.txt new file mode 100644 index 0000000000..7ac7981294 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0.txt index b663f4566b..725bc85472 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0.txt @@ -1,7 +1,6 @@ anyio==3.7.1 asgiref==3.8.1 certifi==2024.7.4 -Deprecated==1.2.14 exceptiongroup==1.2.0 h11==0.12.0 httpcore==0.13.7 @@ -9,7 +8,7 @@ httpx==0.18.2 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 respx==0.17.1 @@ -17,7 +16,6 @@ rfc3986==1.5.0 sniffio==1.3.1 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e util/opentelemetry-util-http diff --git a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt1.txt new file mode 100644 index 0000000000..eb6190d00d --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt2.txt new file mode 100644 index 0000000000..eaac680e2e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt index e8039e691c..f0ca4be8b5 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt @@ -1,7 +1,6 @@ anyio==4.3.0 asgiref==3.8.1 certifi==2024.7.4 -Deprecated==1.2.14 exceptiongroup==1.2.0 h11==0.16 httpcore==1.0.9 @@ -9,14 +8,13 @@ httpx==0.28.1 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 respx==0.22.0 sniffio==1.3.1 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e util/opentelemetry-util-http diff --git a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py index 7b0d3164f9..91ade3a94a 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py @@ -22,7 +22,12 @@ import httpx import respx -from wrapt import ObjectProxy + +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy import opentelemetry.instrumentation.httpx from opentelemetry import trace @@ -1482,7 +1487,7 @@ def assert_proxy_mounts(self, mounts, num_mounts, transport_type=None): else: handler = self.get_transport_handler(transport) self.assertTrue( - isinstance(handler, ObjectProxy) + isinstance(handler, BaseObjectProxy) and getattr(handler, "__wrapped__") ) @@ -2082,13 +2087,13 @@ class CustomClient(httpx.Client): client = CustomClient() self.assertFalse( - isinstance(client._transport.handle_request, ObjectProxy) + isinstance(client._transport.handle_request, BaseObjectProxy) ) HTTPXClientInstrumentor().instrument() self.assertTrue( - isinstance(client._transport.handle_request, ObjectProxy) + isinstance(client._transport.handle_request, BaseObjectProxy) ) @@ -2177,11 +2182,11 @@ class CustomAsyncClient(httpx.AsyncClient): client = CustomAsyncClient() self.assertFalse( - isinstance(client._transport.handle_async_request, ObjectProxy) + isinstance(client._transport.handle_async_request, BaseObjectProxy) ) HTTPXClientInstrumentor().instrument() self.assertTrue( - isinstance(client._transport.handle_async_request, ObjectProxy) + isinstance(client._transport.handle_async_request, BaseObjectProxy) ) diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/pyproject.toml b/instrumentation/opentelemetry-instrumentation-jinja2/pyproject.toml index c2b6d2592e..70f36f5030 100644 --- a/instrumentation/opentelemetry-instrumentation-jinja2/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-jinja2/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry jinja2 instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/version.py b/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/version.py +++ b/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-jinja2/test-requirements.txt index a4f8145b1e..dbbd4b6d8e 100644 --- a/instrumentation/opentelemetry-instrumentation-jinja2/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-jinja2/test-requirements.txt @@ -4,7 +4,7 @@ iniconfig==2.0.0 Jinja2==3.1.6 MarkupSafe==2.0.1 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-kafka-python/pyproject.toml b/instrumentation/opentelemetry-instrumentation-kafka-python/pyproject.toml index f84b48299b..141c72c56f 100644 --- a/instrumentation/opentelemetry-instrumentation-kafka-python/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-kafka-python/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Kafka-Python instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.5", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-kafka-python/src/opentelemetry/instrumentation/kafka/version.py b/instrumentation/opentelemetry-instrumentation-kafka-python/src/opentelemetry/instrumentation/kafka/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-kafka-python/src/opentelemetry/instrumentation/kafka/version.py +++ b/instrumentation/opentelemetry-instrumentation-kafka-python/src/opentelemetry/instrumentation/kafka/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements-ng.txt b/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements-ng.txt index 83cef19c4f..f989019867 100644 --- a/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements-ng.txt +++ b/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements-ng.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 kafka-python-ng==2.2.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements.txt index 87752e3542..14975e09c3 100644 --- a/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-kafka-python/test-requirements.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 kafka-python==2.0.2 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-logging/pyproject.toml b/instrumentation/opentelemetry-instrumentation-logging/pyproject.toml index 71a9b31e4e..f14d2e90ea 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-logging/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Logging instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,7 +26,7 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/handler.py b/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/handler.py index c9a890db8b..44ba3164ad 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/handler.py +++ b/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/handler.py @@ -18,6 +18,7 @@ import logging.config import threading import traceback +from contextvars import ContextVar from time import time_ns from typing import Callable @@ -35,6 +36,10 @@ from opentelemetry.semconv.attributes import exception_attributes from opentelemetry.util.types import _ExtendedAttributes +_internal_logger = logging.getLogger(__name__ + ".internal") +_internal_logger.propagate = False +_internal_logger.addHandler(logging.StreamHandler()) + def _setup_logging_handler( logger_provider: LoggerProvider, log_code_attributes: bool = False @@ -112,6 +117,8 @@ class LoggingHandler(logging.Handler): https://docs.python.org/3/library/logging.html """ + _is_emitting: ContextVar[bool] = ContextVar("_is_emitting", default=False) + def __init__( self, level: int = logging.NOTSET, @@ -181,10 +188,14 @@ def _translate(self, record: logging.LogRecord) -> LogRecord: else: body = record.getMessage() - # related to https://github.com/open-telemetry/opentelemetry-python/issues/3548 - # Severity Text = WARN as defined in https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#displaying-severity. - level_name = ( - "WARN" if record.levelname == "WARNING" else record.levelname + # Map Python log level names to OTel severity text as defined in + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#displaying-severity + _python_to_otel_severity_text = { + "WARNING": "WARN", + "CRITICAL": "FATAL", + } + level_name = _python_to_otel_severity_text.get( + record.levelname, record.levelname ) return LogRecord( @@ -203,9 +214,28 @@ def emit(self, record: logging.LogRecord) -> None: The record is translated to OTel format, and then sent across the pipeline. """ - logger = get_logger(record.name, logger_provider=self._logger_provider) - if not isinstance(logger, NoOpLogger): - logger.emit(self._translate(record)) + # Prevent recursive logging that can cause infinite recursion or deadlock. + # During _translate(), internal OTel code (e.g., _clean_extended_attribute) + # may call _logger.warning() for invalid attributes. If the OTel + # LoggingHandler is in the logger chain, this warning re-enters emit(), + # creating an infinite loop that prevents the handler lock from ever + # being released, blocking all other threads. + # See: https://github.com/open-telemetry/opentelemetry-python/issues/3858 + + if self._is_emitting.get(): + _internal_logger.warning( + "LoggingHandler.emit detected recursive logging, skipping to prevent deadlock." + ) + return + token = self._is_emitting.set(True) + try: + logger = get_logger( + record.name, logger_provider=self._logger_provider + ) + if not isinstance(logger, NoOpLogger): + logger.emit(self._translate(record)) + finally: + self._is_emitting.reset(token) def flush(self) -> None: """ diff --git a/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/version.py b/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/version.py index 46b492f255..d21c60d918 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/version.py +++ b/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/version.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" _instruments = tuple() diff --git a/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt index 2512824c23..8d6098d624 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-logging/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-logging/tests/test_handler.py b/instrumentation/opentelemetry-instrumentation-logging/tests/test_handler.py index 68c8daaa24..0d15af33d4 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/tests/test_handler.py +++ b/instrumentation/opentelemetry-instrumentation-logging/tests/test_handler.py @@ -357,7 +357,7 @@ def test_log_record_trace_correlation(self): record.log_record.body, "Critical message within span", ) - self.assertEqual(record.log_record.severity_text, "CRITICAL") + self.assertEqual(record.log_record.severity_text, "FATAL") self.assertEqual( record.log_record.severity_number, SeverityNumber.FATAL, @@ -390,7 +390,7 @@ def test_log_record_trace_correlation_deprecated(self): self.assertEqual( record.log_record.body, "Critical message within span" ) - self.assertEqual(record.log_record.severity_text, "CRITICAL") + self.assertEqual(record.log_record.severity_text, "FATAL") self.assertEqual( record.log_record.severity_number, SeverityNumber.FATAL ) diff --git a/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging_handler_recursion.py b/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging_handler_recursion.py new file mode 100644 index 0000000000..3afc9986b1 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-logging/tests/test_logging_handler_recursion.py @@ -0,0 +1,91 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import threading +import unittest + +from opentelemetry.instrumentation.logging.handler import LoggingHandler +from opentelemetry.sdk._logs import LoggerProvider +from opentelemetry.sdk._logs.export import ( + InMemoryLogExporter, + SimpleLogRecordProcessor, +) + + +class TestLoggingHandlerRecursionGuard(unittest.TestCase): + @staticmethod + def _create_handler(): + exporter = InMemoryLogExporter() + provider = LoggerProvider() + provider.add_log_record_processor(SimpleLogRecordProcessor(exporter)) + handler = LoggingHandler(logger_provider=provider) + return handler, exporter + + @staticmethod + def _make_record(msg="test"): + return logging.LogRecord( + name="test", + level=logging.INFO, + pathname="", + lineno=0, + msg=msg, + args=(), + exc_info=None, + ) + + def test_recursive_emit_is_skipped(self): + """Recursive emit() should be skipped to prevent deadlock. + Regression test for https://github.com/open-telemetry/opentelemetry-python/issues/3858 + """ + handler, exporter = self._create_handler() + token = handler._is_emitting.set(True) + handler.emit(self._make_record("should be skipped")) + self.assertEqual(len(exporter.get_finished_logs()), 0) + handler._is_emitting.reset(token) + + def test_normal_emit_works(self): + """Non-recursive emit() should process logs normally.""" + handler, exporter = self._create_handler() + handler.emit(self._make_record("should be captured")) + logs = exporter.get_finished_logs() + self.assertEqual(len(logs), 1) + self.assertEqual(logs[0].log_record.body, "should be captured") + + def test_is_emitting_resets_after_emit(self): + """Context must reset after emit(), allowing subsequent logs.""" + handler, exporter = self._create_handler() + handler.emit(self._make_record("first")) + self.assertFalse(handler._is_emitting.get()) + handler.emit(self._make_record("second")) + self.assertEqual(len(exporter.get_finished_logs()), 2) + + def test_is_emitting_is_isolated_across_threads(self): + """Emit context on one thread must not block other threads.""" + handler, exporter = self._create_handler() + token = handler._is_emitting.set(True) + + result = [False] + + def log_from_other_thread(): + handler.emit(self._make_record("from other thread")) + result[0] = True + + thread = threading.Thread(target=log_from_other_thread) + thread.start() + thread.join(timeout=5) + + self.assertTrue(result[0], "Other thread should not be blocked") + self.assertEqual(len(exporter.get_finished_logs()), 1) + handler._is_emitting.reset(token) diff --git a/instrumentation/opentelemetry-instrumentation-mysql/pyproject.toml b/instrumentation/opentelemetry-instrumentation-mysql/pyproject.toml index bd143731bc..f58c79a4dd 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-mysql/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry MySQL instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/version.py b/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/version.py +++ b/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-0.txt index 45fb95cb37..05d78ab5ca 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-0.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 mysql-connector-python==8.3.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-1.txt index 0ca8efb64c..df8ed74d44 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-mysql/test-requirements-1.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 mysql-connector-python==9.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/pyproject.toml b/instrumentation/opentelemetry-instrumentation-mysqlclient/pyproject.toml index ac54f38f84..4d2ba6aca3 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry mysqlclient instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/version.py b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/version.py +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt index 0abcd23bd2..6cb821c22e 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/test-requirements.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 mysqlclient==2.2.4 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-pika/pyproject.toml b/instrumentation/opentelemetry-instrumentation-pika/pyproject.toml index 1658d67100..8c7c643696 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-pika/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry pika instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,10 +25,10 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-instrumentation == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", "opentelemetry-api ~= 1.5", "packaging >= 20.0", - "wrapt >= 1.0.0, < 2.0.0", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py index 7b84f89229..8935d23950 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/pika_instrumentor.py @@ -60,6 +60,7 @@ def _instrument_channel_consumers( tracer: Tracer, consume_hook: utils.HookT = utils.dummy_callback, ) -> Any: + consumer_infos = None if isinstance(channel, BlockingChannel): consumer_infos = channel._consumer_infos elif isinstance(channel, Channel): diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py index ff3ac86ac5..b7f1117429 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/utils.py @@ -7,7 +7,12 @@ ) from pika.channel import Channel from pika.spec import Basic, BasicProperties -from wrapt import ObjectProxy + +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy from opentelemetry import context, propagate, trace from opentelemetry.instrumentation.utils import is_instrumentation_enabled @@ -196,7 +201,7 @@ def _enrich_span( # pylint:disable=abstract-method -class ReadyMessagesDequeProxy(ObjectProxy): +class ReadyMessagesDequeProxy(BaseObjectProxy): def __init__( self, wrapped, @@ -218,7 +223,7 @@ def popleft(self, *args, **kwargs): except Exception as inst_exception: # pylint: disable=W0703 _LOG.exception(inst_exception) - evt = self.__wrapped__.popleft(*args, **kwargs) + evt = self.__wrapped__.popleft(*args, **kwargs) # pylint:disable=no-member try: # If a new message was received, create a span and set as active context diff --git a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/version.py b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/version.py +++ b/instrumentation/opentelemetry-instrumentation-pika/src/opentelemetry/instrumentation/pika/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt1.txt new file mode 100644 index 0000000000..57149ca01e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt2.txt new file mode 100644 index 0000000000..7ac7981294 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-0.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0.txt index 3e2f26b098..697998ce83 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0.txt @@ -1,14 +1,12 @@ asgiref==3.8.1 -Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 pika==0.13.1 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-pika diff --git a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt1.txt b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt1.txt new file mode 100644 index 0000000000..eb6190d00d --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==1.17.3 diff --git a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt2.txt b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt2.txt new file mode 100644 index 0000000000..eaac680e2e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements-1.txt +wrapt==2.1.2 diff --git a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt index 9ad521943f..645689b674 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt @@ -1,14 +1,12 @@ asgiref==3.8.1 -Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 pika==1.3.2 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation -e instrumentation/opentelemetry-instrumentation-pika diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/pyproject.toml b/instrumentation/opentelemetry-instrumentation-psycopg/pyproject.toml index 910619d389..ace2acb412 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-psycopg/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry psycopg instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/version.py b/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/version.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-0.txt index a68ae0b797..d28af01d88 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-0.txt @@ -3,7 +3,7 @@ backports.zoneinfo==0.2.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 psycopg==3.1.18 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt index f18f4522a9..c254e24834 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 psycopg==3.2.2 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/pyproject.toml b/instrumentation/opentelemetry-instrumentation-psycopg2/pyproject.toml index c18c5bb977..b96dfe2314 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry psycopg2 instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py index d1753af6a9..4c8b1b6e02 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py @@ -136,6 +136,19 @@ Warning: Capture of sqlcomment in ``db.statement`` may have high cardinality without platform normalization. See `Semantic Conventions for database spans `_ for more information. +Capture parameters +****************** +By default, only statements are captured, without the associated query parameters. +To capture query parameters in the span attribute `db.statement.parameters`, enable `capture_parameters`. + +.. code:: python + + from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor + + Psycopg2Instrumentor().instrument( + capture_parameters=True, + ) + API --- """ @@ -213,6 +226,7 @@ def _instrument(self, **kwargs): enable_attribute_commenter = kwargs.get( "enable_attribute_commenter", False ) + capture_parameters = kwargs.get("capture_parameters", False) dbapi.wrap_connect( __name__, psycopg2, @@ -225,6 +239,7 @@ def _instrument(self, **kwargs): enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, enable_attribute_commenter=enable_attribute_commenter, + capture_parameters=capture_parameters, ) def _uninstrument(self, **kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/version.py b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/version.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements-binary.txt b/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements-binary.txt index 80fa036b99..f47e576ea1 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements-binary.txt +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements-binary.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 psycopg2-binary==2.9.10 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements.txt index 305185e362..8171330f1d 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 psycopg2==2.9.10 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_instrumentation.py b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_instrumentation.py index 93a17d9ed1..27b8618852 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_instrumentation.py @@ -15,14 +15,21 @@ from unittest import TestCase from unittest.mock import Mock, call, patch +import psycopg2 + from opentelemetry.instrumentation.auto_instrumentation._load import ( _load_instrumentors, ) -from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor +from opentelemetry.instrumentation.psycopg2 import ( + DatabaseApiIntegration, + Psycopg2Instrumentor, +) from opentelemetry.instrumentation.psycopg2.package import ( _instruments_psycopg2, _instruments_psycopg2_binary, ) +from opentelemetry.instrumentation.psycopg2.version import __version__ +from opentelemetry.test.test_base import TestBase class TestPsycopg2InstrumentationDependencies(TestCase): @@ -160,3 +167,41 @@ def _instrumentation_loaded_successfully_call(): mock_logger.debug.assert_has_calls( [_instrumentation_loaded_successfully_call()] ) + + +@patch("opentelemetry.instrumentation.psycopg2.dbapi") +class TestPsycopg2InstrumentorParameters(TestBase): + def tearDown(self): + super().tearDown() + with self.disable_logging(): + Psycopg2Instrumentor().uninstrument() + + def test_instrument_defaults(self, mock_dbapi): # pylint: disable=no-self-use + Psycopg2Instrumentor().instrument() + + mock_dbapi.wrap_connect.assert_called_once_with( + "opentelemetry.instrumentation.psycopg2", + psycopg2, + "connect", + "postgresql", + { + "database": "info.dbname", + "port": "info.port", + "host": "info.host", + "user": "info.user", + }, + version=__version__, + tracer_provider=None, + db_api_integration_factory=DatabaseApiIntegration, + enable_commenter=False, + commenter_options={}, + enable_attribute_commenter=False, + capture_parameters=False, + ) + + def test_instrument_capture_parameters(self, mock_dbapi): + Psycopg2Instrumentor().instrument(capture_parameters=True) + + self.assertTrue( + mock_dbapi.wrap_connect.call_args.kwargs["capture_parameters"] + ) diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py index a1477278db..2b985dc180 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py @@ -371,3 +371,24 @@ def test_no_op_tracer_provider(self): cursor.execute(query) spans_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans_list), 0) + + def test_span_capture_params_deactivated_by_default(self): + Psycopg2Instrumentor().instrument() + cnx = psycopg2.connect(database="test") + cursor = cnx.cursor() + query = "SELECT * FROM test WHERE id = %s AND name = %s" + cursor.execute(query, (42, "John Doe")) + spans_list = self.memory_exporter.get_finished_spans() + self.assertNotIn("db.statement.parameters", spans_list[0].attributes) + + def test_span_capture_params_activated(self): + Psycopg2Instrumentor().instrument(capture_parameters=True) + cnx = psycopg2.connect(database="test") + cursor = cnx.cursor() + query = "SELECT * FROM test WHERE id = %s AND name = %s" + cursor.execute(query, (42, "John Doe")) + spans_list = self.memory_exporter.get_finished_spans() + self.assertEqual( + spans_list[0].attributes["db.statement.parameters"], + "(42, 'John Doe')", + ) diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/pyproject.toml b/instrumentation/opentelemetry-instrumentation-pymemcache/pyproject.toml index aa34660048..fcb0dfd74a 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry pymemcache instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/version.py b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/version.py +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-0.txt index d1b214c595..9379eb3087 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-0.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pymemcache==1.3.5 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-1.txt index 64224bc7a5..02515b7c0c 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-1.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pymemcache==2.2.2 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-2.txt index d54b48ffea..ccaf5fb9d1 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-2.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pymemcache==3.4.1 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-3.txt b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-3.txt index 509bdda5a6..37240eb242 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-3.txt +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-3.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pymemcache==3.4.2 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt index 93e1c041d5..c3b002f3c7 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/test-requirements-4.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pymemcache==4.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/pyproject.toml b/instrumentation/opentelemetry-instrumentation-pymongo/pyproject.toml index f714361b0e..759c9b8649 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-pymongo/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry pymongo instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/version.py b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/version.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt index 1e8a49d67d..962d918f3e 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 dnspython==2.6.1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pymongo==4.6.3 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-pymssql/pyproject.toml b/instrumentation/opentelemetry-instrumentation-pymssql/pyproject.toml index 317b68f5ad..a97c708b15 100644 --- a/instrumentation/opentelemetry-instrumentation-pymssql/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-pymssql/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry pymssql instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-pymssql/src/opentelemetry/instrumentation/pymssql/version.py b/instrumentation/opentelemetry-instrumentation-pymssql/src/opentelemetry/instrumentation/pymssql/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-pymssql/src/opentelemetry/instrumentation/pymssql/version.py +++ b/instrumentation/opentelemetry-instrumentation-pymssql/src/opentelemetry/instrumentation/pymssql/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-pymssql/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-pymssql/test-requirements.txt index 6b5972aafb..ba53d09622 100644 --- a/instrumentation/opentelemetry-instrumentation-pymssql/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-pymssql/test-requirements.txt @@ -1,7 +1,7 @@ exceptiongroup==1.2.2 iniconfig==2.0.0 packaging==24.2 -pluggy==1.5.0 +pluggy==1.6.0 pymssql==2.3.12 pytest==7.4.4 tomli==2.2.1 diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/pyproject.toml b/instrumentation/opentelemetry-instrumentation-pymysql/pyproject.toml index 1310aec93f..43d72e03ff 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-pymysql/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry PyMySQL instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/version.py b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/version.py +++ b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-pymysql/test-requirements.txt index 02018b0c5e..759647c581 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-pymysql/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 PyMySQL==1.1.1 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/pyproject.toml b/instrumentation/opentelemetry-instrumentation-pyramid/pyproject.toml index 2f742f0e5b..0268d0cd02 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-pyramid/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Pyramid instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -28,11 +27,11 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-wsgi == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-wsgi == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py index 68d51bf155..c665fdc3f8 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py @@ -148,7 +148,7 @@ def _before_traversal(event): span.set_attributes(custom_attributes) activation = trace.use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call request_environ[_ENVIRON_ACTIVATION_KEY] = activation request_environ[_ENVIRON_SPAN_KEY] = span if token: @@ -212,7 +212,7 @@ def disabled_tween(request): # make a request tracing function # pylint: disable=too-many-branches def trace_tween(request): - # pylint: disable=E1101, too-many-locals + # pylint: disable=unnecessary-dunder-call, too-many-locals if _excluded_urls.url_disabled(request.url): request.environ[_ENVIRON_ENABLED_KEY] = False # short-circuit when we don't want to trace anything diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/version.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/version.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-pyramid/test-requirements.txt index 40438a9f88..ea22666b4e 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-pyramid/test-requirements.txt @@ -6,7 +6,7 @@ packaging==24.0 PasteDeploy==3.1.0 plaster==1.1.2 plaster-pastedeploy==1.0.1 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pyramid==2.0.2 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-redis/pyproject.toml b/instrumentation/opentelemetry-instrumentation-redis/pyproject.toml index 77a2496ba6..19c5e194f9 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-redis/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Redis instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", "wrapt >= 1.12.1", ] diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py index 8a4f93329d..77c3ac31c3 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py @@ -698,7 +698,6 @@ def uninstrument_client( _logger.warning( "Attempting to un-instrument Redis connection that wasn't instrumented" ) - return def instrumentation_dependencies(self) -> Collection[str]: """Return a list of python packages with versions that the will be instrumented.""" diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/version.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/version.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-redis/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-redis/test-requirements.txt index 6beac4a9cc..627b13573c 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-redis/test-requirements.txt @@ -4,7 +4,7 @@ Deprecated==1.2.14 fakeredis==2.23.3 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 redis==5.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-remoulade/pyproject.toml b/instrumentation/opentelemetry-instrumentation-remoulade/pyproject.toml index 9aac495d33..199bee6b0f 100644 --- a/instrumentation/opentelemetry-instrumentation-remoulade/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-remoulade/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Remoulade instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/__init__.py b/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/__init__.py index 28a41b6a6c..8a75448f48 100644 --- a/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/__init__.py @@ -90,7 +90,7 @@ def before_process_message(self, _broker, message): ) activation = trace.use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call utils.attach_span( self._span_registry, message.message_id, (span, activation) @@ -142,7 +142,7 @@ def before_enqueue(self, _broker, message, delay): ) activation = trace.use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call utils.attach_span( self._span_registry, diff --git a/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/version.py b/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/version.py +++ b/instrumentation/opentelemetry-instrumentation-remoulade/src/opentelemetry/instrumentation/remoulade/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-remoulade/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-remoulade/test-requirements.txt index bfa53ed1f8..42ee69de5a 100644 --- a/instrumentation/opentelemetry-instrumentation-remoulade/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-remoulade/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 prometheus_client==0.20.0 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-requests/pyproject.toml b/instrumentation/opentelemetry-instrumentation-requests/pyproject.toml index 445e42e327..7f18d26879 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-requests/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry requests instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/version.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/version.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt index 0b8d7ada10..fb2a7a13c4 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt @@ -6,7 +6,7 @@ httpretty==1.1.4 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 requests==2.32.3 diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/pyproject.toml b/instrumentation/opentelemetry-instrumentation-sqlalchemy/pyproject.toml index c6a4c570b5..34317db4b0 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry SQLAlchemy instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", "packaging >= 21.0", "wrapt >= 1.11.2", ] diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/__init__.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/__init__.py index a95266351f..dbcffc5ecf 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/__init__.py @@ -110,16 +110,16 @@ SQLComment in span attribute **************************** If sqlcommenter is enabled, you can opt into the inclusion of sqlcomment in -the query span ``db.statement`` attribute for your needs. If ``commenter_options`` -have been set, the span attribute comment will also be configured by this -setting. +the query span ``db.statement`` and/or ``db.query.text`` attribute for your +needs. If ``commenter_options`` have been set, the span attribute comment +will also be configured by this setting. .. code:: python from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor # Opts into sqlcomment for SQLAlchemy trace integration. - # Opts into sqlcomment for `db.statement` span attribute. + # Opts into sqlcomment for `db.statement` and/or `db.query.text` span attribute. SQLAlchemyInstrumentor().instrument( enable_commenter=True, commenter_options={}, @@ -127,7 +127,7 @@ ) Warning: - Capture of sqlcomment in ``db.statement`` may have high cardinality without platform normalization. See `Semantic Conventions for database spans `_ for more information. + Capture of sqlcomment in ``db.statement``/``db.query.text`` may have high cardinality without platform normalization. See `Semantic Conventions for database spans `_ for more information. API --- @@ -141,6 +141,11 @@ from sqlalchemy.engine.base import Engine from wrapt import wrap_function_wrapper as _w +from opentelemetry.instrumentation._semconv import ( + _get_schema_url_for_signal_types, + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, +) from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.sqlalchemy.engine import ( EngineTracer, @@ -181,12 +186,24 @@ def _instrument(self, **kwargs): Returns: An instrumented engine if passed in as an argument or list of instrumented engines, None otherwise. """ + # Initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + + # Determine schema URL based on both DATABASE and HTTP signal types + # and semconv opt-in mode + schema_url = _get_schema_url_for_signal_types( + [ + _OpenTelemetryStabilitySignalType.DATABASE, + _OpenTelemetryStabilitySignalType.HTTP, + ] + ) + tracer_provider = kwargs.get("tracer_provider") tracer = get_tracer( __name__, __version__, tracer_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", + schema_url=schema_url, ) meter_provider = kwargs.get("meter_provider") @@ -194,7 +211,7 @@ def _instrument(self, **kwargs): __name__, __version__, meter_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", + schema_url=schema_url, ) connections_usage = meter.create_up_down_counter( diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py index 6a380a3221..d357798ffa 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py @@ -22,26 +22,91 @@ ) from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, + _set_db_name, + _set_db_operation, + _set_db_statement, + _set_db_system, + _set_db_user, + _set_http_net_peer_name_client, + _set_http_peer_port_client, +) from opentelemetry.instrumentation.sqlcommenter_utils import _add_sql_comment from opentelemetry.instrumentation.utils import ( _get_opentelemetry_values, is_instrumentation_enabled, ) -from opentelemetry.semconv._incubating.attributes.db_attributes import ( - DB_NAME, - DB_STATEMENT, - DB_SYSTEM, - DB_USER, -) from opentelemetry.semconv._incubating.attributes.net_attributes import ( - NET_PEER_NAME, - NET_PEER_PORT, NET_TRANSPORT, NetTransportValues, ) from opentelemetry.trace.status import Status, StatusCode +def _get_db_name_from_cursor_or_conn(vendor, conn, cursor): + """Return DB name from cursor or connection when available -- else None.""" + if not vendor: + return None + + vendor = vendor.lower() + db_name = None + if "postgres" in vendor: + info = getattr(getattr(cursor, "connection", None), "info", None) + if info and hasattr(info, "dbname"): + db_name = info.dbname + elif "mysql" in vendor: + db_name = _get_mysql_db_name(cursor) + elif "mssql" in vendor or "sqlserver" in vendor: + db_name = _get_mssql_db_name(cursor) + if not db_name: + engine = getattr(conn, "engine", None) + url = getattr(engine, "url", None) + db_name = getattr(url, "database", None) + else: + # Try connection for sqlite and others + engine = getattr(conn, "engine", None) + url = getattr(engine, "url", None) + db_name = getattr(url, "database", None) + return db_name + + +def _get_mysql_db_name(cursor): + """Extract database name from MySQL cursor.""" + # mysql-connector with c-extension uses _cnx + connection = getattr(cursor, "connection", None) or getattr( + cursor, "_cnx", None + ) + if not connection: + return None + if hasattr(connection, "database"): + return connection.database + if hasattr(connection, "db"): + raw_db_name = connection.db + return ( + raw_db_name.decode("utf-8") + if isinstance(raw_db_name, bytes) + else raw_db_name + ) + return None + + +def _get_mssql_db_name(cursor): + """Extract database name from MSSQL cursor.""" + connection = getattr(cursor, "connection", None) + if not connection: + return None + if hasattr(connection, "database"): + return connection.database + if hasattr(connection, "db"): + return connection.db + info = getattr(connection, "info", None) + if info and hasattr(info, "database"): + return info.database + return None + + def _normalize_vendor(vendor): """Return a canonical name for a type of database.""" if not vendor: @@ -119,13 +184,30 @@ def _wrap_connect_internal(func, module, args, kwargs): if not is_instrumentation_enabled(): return func(*args, **kwargs) + # Initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + sem_conv_opt_in_mode_db = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.DATABASE, + ) + sem_conv_opt_in_mode_http = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, + ) + with tracer.start_as_current_span( "connect", kind=trace.SpanKind.CLIENT ) as span: if span.is_recording(): - attrs, _ = _get_attributes_from_url(module.url) + attrs, _ = _get_attributes_from_url( + module.url, + sem_conv_opt_in_mode_db, + sem_conv_opt_in_mode_http, + ) + _set_db_system( + attrs, + _normalize_vendor(module.name), + sem_conv_opt_in_mode_db, + ) span.set_attributes(attrs) - span.set_attribute(DB_SYSTEM, _normalize_vendor(module.name)) return func(*args, **kwargs) return _wrap_connect_internal @@ -143,6 +225,15 @@ def __init__( commenter_options=None, enable_attribute_commenter=False, ): + # Initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + self._sem_conv_opt_in_mode_db = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.DATABASE, + ) + self._sem_conv_opt_in_mode_http = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, + ) + self.tracer = tracer self.connections_usage = connections_usage self.vendor = _normalize_vendor(engine.name) @@ -276,11 +367,19 @@ def _get_commenter_data(self, conn) -> dict: } return commenter_data - def _set_db_client_span_attributes(self, span, statement, attrs) -> None: - """Uses statement and attrs to set attributes of provided Otel span""" - span.set_attribute(DB_STATEMENT, statement) - span.set_attribute(DB_SYSTEM, self.vendor) - for key, value in attrs.items(): + def _set_db_client_span_attributes( + self, span, statement, db_name, attrs + ) -> None: + """Uses statement, db_name, and attrs to set attributes of provided Otel span""" + span_attrs = dict(attrs) + _set_db_statement(span_attrs, statement, self._sem_conv_opt_in_mode_db) + _set_db_system(span_attrs, self.vendor, self._sem_conv_opt_in_mode_db) + _set_db_operation( + span_attrs, + self._operation_name(db_name, statement), + self._sem_conv_opt_in_mode_db, + ) + for key, value in span_attrs.items(): span.set_attribute(key, value) def _before_cur_exec( @@ -289,11 +388,24 @@ def _before_cur_exec( if not is_instrumentation_enabled(): return statement, params - attrs, found = _get_attributes_from_url(conn.engine.url) + attrs, found = _get_attributes_from_url( + conn.engine.url, + self._sem_conv_opt_in_mode_db, + self._sem_conv_opt_in_mode_http, + ) if not found: - attrs = _get_attributes_from_cursor(self.vendor, cursor, attrs) + attrs = _get_attributes_from_cursor_or_conn( + self.vendor, + conn, + cursor, + attrs, + self._sem_conv_opt_in_mode_db, + self._sem_conv_opt_in_mode_http, + ) + + # Extract db_name for operation name + db_name = _get_db_name_from_cursor_or_conn(self.vendor, conn, cursor) - db_name = attrs.get(DB_NAME, "") span = self.tracer.start_span( self._operation_name(db_name, statement), kind=trace.SpanKind.CLIENT, @@ -307,19 +419,19 @@ def _before_cur_exec( # just to handle type safety statement = str(statement) - # sqlcomment is added to executed query and db.statement span attribute + # sqlcomment is added to executed query and db.statement and/or db.query.text span attribute statement = _add_sql_comment( statement, **commenter_data ) self._set_db_client_span_attributes( - span, statement, attrs + span, statement, db_name, attrs ) else: # sqlcomment is only added to executed query - # so db.statement is set before add_sql_comment + # so db.statement and/or db.query.text is set before add_sql_comment self._set_db_client_span_attributes( - span, statement, attrs + span, statement, db_name, attrs ) statement = _add_sql_comment( statement, **commenter_data @@ -327,7 +439,9 @@ def _before_cur_exec( else: # no sqlcomment anywhere - self._set_db_client_span_attributes(span, statement, attrs) + self._set_db_client_span_attributes( + span, statement, db_name, attrs + ) context._otel_span = span @@ -358,42 +472,64 @@ def _handle_error(context): span.end() -def _get_attributes_from_url(url): +def _get_attributes_from_url( + url, sem_conv_opt_in_mode_db, sem_conv_opt_in_mode_http +): """Set connection tags from the url. return true if successful.""" attrs = {} if url.host: - attrs[NET_PEER_NAME] = url.host + _set_http_net_peer_name_client( + attrs, url.host, sem_conv_opt_in_mode_http + ) if url.port: - attrs[NET_PEER_PORT] = url.port + _set_http_peer_port_client(attrs, url.port, sem_conv_opt_in_mode_http) if url.database: - attrs[DB_NAME] = url.database + _set_db_name(attrs, url.database, sem_conv_opt_in_mode_db) if url.username: - attrs[DB_USER] = url.username + _set_db_user(attrs, url.username, sem_conv_opt_in_mode_db) return attrs, bool(url.host) -def _get_attributes_from_cursor(vendor, cursor, attrs): +def _get_attributes_from_cursor_or_conn( + vendor, + conn, + cursor, + attrs, + sem_conv_opt_in_mode_db, + sem_conv_opt_in_mode_http, +): """Attempt to set db connection attributes by introspecting the cursor.""" if vendor == "postgresql": info = getattr(getattr(cursor, "connection", None), "info", None) if not info: return attrs - attrs[DB_NAME] = info.dbname + db_name = _get_db_name_from_cursor_or_conn(vendor, conn, cursor) + _set_db_name(attrs, db_name, sem_conv_opt_in_mode_db) is_unix_socket = info.host and info.host.startswith("/") if is_unix_socket: attrs[NET_TRANSPORT] = NetTransportValues.OTHER.value if info.port: # postgresql enforces this pattern on all socket names - attrs[NET_PEER_NAME] = os.path.join( - info.host, f".s.PGSQL.{info.port}" + _set_http_net_peer_name_client( + attrs, + os.path.join(info.host, f".s.PGSQL.{info.port}"), + sem_conv_opt_in_mode_http, ) else: attrs[NET_TRANSPORT] = NetTransportValues.IP_TCP.value - attrs[NET_PEER_NAME] = info.host + _set_http_net_peer_name_client( + attrs, info.host, sem_conv_opt_in_mode_http + ) if info.port: - attrs[NET_PEER_PORT] = int(info.port) + _set_http_peer_port_client( + attrs, int(info.port), sem_conv_opt_in_mode_http + ) + elif vendor == "sqlite": + db_name = _get_db_name_from_cursor_or_conn(vendor, conn, cursor) + _set_db_name(attrs, db_name, sem_conv_opt_in_mode_db) + # SQLite has no network attributes return attrs diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/package.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/package.py index 77b148e51c..a95b8a6c76 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/package.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/package.py @@ -15,3 +15,5 @@ _instruments = ("sqlalchemy >= 1.0.0, < 2.1.0",) _supports_metrics = True + +_semconv_status = "migration" diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/version.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/version.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt index e3f87916c2..3bf49c244e 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-0.txt @@ -3,7 +3,7 @@ cffi==1.17.0 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 SQLAlchemy==1.1.18 diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt index 13ea334c4d..cfecf5b91d 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-1.txt @@ -4,7 +4,7 @@ Deprecated==1.2.14 greenlet==3.1.1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 SQLAlchemy==1.4.51 diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-2.txt b/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-2.txt index 9193b5996a..c628f928d6 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-2.txt +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/test-requirements-2.txt @@ -5,7 +5,7 @@ greenlet==3.1.1 ; python_version < "3.10" greenlet==3.3.1 ; python_version >= "3.10" iniconfig==2.0.0 packaging==24.1 -pluggy==1.5.0 +pluggy==1.6.0 pytest==7.4.4 SQLAlchemy==2.0.36 typing_extensions==4.12.2 diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py index 882659daf7..36257d9d32 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py @@ -23,18 +23,35 @@ ) from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + OTEL_SEMCONV_STABILITY_OPT_IN, + _OpenTelemetrySemanticConventionStability, +) from opentelemetry.instrumentation.sqlalchemy import ( EngineTracer, SQLAlchemyInstrumentor, ) +from opentelemetry.instrumentation.sqlalchemy.engine import ( + _get_db_name_from_cursor_or_conn, +) from opentelemetry.instrumentation.utils import suppress_instrumentation from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider, export from opentelemetry.semconv._incubating.attributes.db_attributes import ( DB_NAME, + DB_OPERATION, DB_STATEMENT, DB_SYSTEM, ) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, +) +from opentelemetry.semconv.attributes.db_attributes import ( + DB_NAMESPACE, + DB_OPERATION_NAME, + DB_QUERY_TEXT, + DB_SYSTEM_NAME, +) from opentelemetry.test.test_base import TestBase @@ -43,6 +60,12 @@ class TestSqlalchemyInstrumentation(TestBase): def inject_fixtures(self, caplog): self.caplog = caplog # pylint: disable=attribute-defined-outside-init + def setUp(self): + super().setUp() + # Reset semconv state before each test for reproducibility + # Tests using @mock.patch.dict will re-initialize again after the mock is applied + _OpenTelemetrySemanticConventionStability._initialized = False + def tearDown(self): super().tearDown() SQLAlchemyInstrumentor().uninstrument() @@ -687,6 +710,276 @@ def test_suppress_instrumentation_connect(self): spans_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans_list), 0) + def test_semconv_default_mode(self): + SQLAlchemyInstrumentor().uninstrument() + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1 + 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + + connect_span = spans[0] + self.assertEqual( + connect_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.11.0", + ) + self.assertIn(DB_SYSTEM, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM], "sqlite") + self.assertIn(DB_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAME], ":memory:") + # Verify new conventions are NOT present + self.assertNotIn(DB_SYSTEM_NAME, connect_span.attributes) + self.assertNotIn(DB_NAMESPACE, connect_span.attributes) + + query_span = spans[1] + self.assertIn(DB_STATEMENT, query_span.attributes) + self.assertIn(DB_SYSTEM, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM], "sqlite") + self.assertIn(DB_OPERATION, query_span.attributes) + self.assertEqual( + query_span.attributes[DB_OPERATION], "SELECT :memory:" + ) + # Verify new conventions are NOT present + self.assertNotIn(DB_QUERY_TEXT, query_span.attributes) + self.assertNotIn(DB_SYSTEM_NAME, query_span.attributes) + + @mock.patch.dict("os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "http"}) + def test_semconv_http_mode(self): + SQLAlchemyInstrumentor().uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1 + 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + + connect_span = spans[0] + self.assertEqual( + connect_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.21.0", + ) + # HTTP attributes should use new semconv + self.assertNotIn(NET_PEER_NAME, connect_span.attributes) + # DB attributes should still use old semconv + self.assertIn(DB_SYSTEM, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM], "sqlite") + self.assertIn(DB_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAME], ":memory:") + # Verify new DB conventions are NOT present + self.assertNotIn(DB_SYSTEM_NAME, connect_span.attributes) + self.assertNotIn(DB_NAMESPACE, connect_span.attributes) + + query_span = spans[1] + # DB attributes should still use old semconv + self.assertIn(DB_STATEMENT, query_span.attributes) + self.assertIn(DB_SYSTEM, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM], "sqlite") + # Verify new DB conventions are NOT present + self.assertNotIn(DB_QUERY_TEXT, query_span.attributes) + self.assertNotIn(DB_SYSTEM_NAME, query_span.attributes) + + @mock.patch.dict("os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "database"}) + def test_semconv_database_mode(self): + SQLAlchemyInstrumentor().uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1 + 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + + connect_span = spans[0] + self.assertEqual( + connect_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.25.0", + ) + self.assertIn(DB_SYSTEM_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM_NAME], "sqlite") + self.assertIn(DB_NAMESPACE, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAMESPACE], ":memory:") + # Verify old conventions are NOT present + self.assertNotIn(DB_SYSTEM, connect_span.attributes) + self.assertNotIn(DB_NAME, connect_span.attributes) + + query_span = spans[1] + self.assertIn(DB_QUERY_TEXT, query_span.attributes) + self.assertIn(DB_SYSTEM_NAME, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM_NAME], "sqlite") + self.assertIn(DB_OPERATION_NAME, query_span.attributes) + self.assertEqual( + query_span.attributes[DB_OPERATION_NAME], "SELECT :memory:" + ) + # Verify old conventions are NOT present + self.assertNotIn(DB_STATEMENT, query_span.attributes) + self.assertNotIn(DB_SYSTEM, query_span.attributes) + + @mock.patch.dict( + "os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "http,database"} + ) + def test_semconv_http_and_database_mode(self): + SQLAlchemyInstrumentor().uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1 + 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + + connect_span = spans[0] + self.assertEqual( + connect_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.25.0", + ) + # New DB conventions should be present + self.assertIn(DB_SYSTEM_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM_NAME], "sqlite") + self.assertIn(DB_NAMESPACE, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAMESPACE], ":memory:") + # Old DB conventions should NOT be present + self.assertNotIn(DB_SYSTEM, connect_span.attributes) + self.assertNotIn(DB_NAME, connect_span.attributes) + + query_span = spans[1] + # New DB conventions should be present + self.assertIn(DB_QUERY_TEXT, query_span.attributes) + self.assertIn(DB_SYSTEM_NAME, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM_NAME], "sqlite") + # Old DB conventions should NOT be present + self.assertNotIn(DB_STATEMENT, query_span.attributes) + self.assertNotIn(DB_SYSTEM, query_span.attributes) + + @mock.patch.dict("os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "http/dup"}) + def test_semconv_http_dup_mode(self): + SQLAlchemyInstrumentor().uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1 + 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + + connect_span = spans[0] + self.assertEqual( + connect_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.21.0", + ) + # DB attributes should use old semconv only + self.assertIn(DB_SYSTEM, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM], "sqlite") + self.assertIn(DB_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAME], ":memory:") + # Verify new DB conventions are NOT present + self.assertNotIn(DB_SYSTEM_NAME, connect_span.attributes) + self.assertNotIn(DB_NAMESPACE, connect_span.attributes) + + query_span = spans[1] + # DB attributes should use old semconv + self.assertIn(DB_STATEMENT, query_span.attributes) + self.assertIn(DB_SYSTEM, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM], "sqlite") + # Verify new DB conventions are NOT present + self.assertNotIn(DB_QUERY_TEXT, query_span.attributes) + self.assertNotIn(DB_SYSTEM_NAME, query_span.attributes) + + @mock.patch.dict( + "os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "database/dup"} + ) + def test_semconv_database_dup_mode(self): + SQLAlchemyInstrumentor().uninstrument() + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1 + 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + + connect_span = spans[0] + self.assertEqual( + connect_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.25.0", + ) + # Old conventions + self.assertIn(DB_SYSTEM, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM], "sqlite") + self.assertIn(DB_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAME], ":memory:") + # New conventions + self.assertIn(DB_SYSTEM_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM_NAME], "sqlite") + self.assertIn(DB_NAMESPACE, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAMESPACE], ":memory:") + + query_span = spans[1] + # Old conventions + self.assertIn(DB_STATEMENT, query_span.attributes) + self.assertIn(DB_SYSTEM, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM], "sqlite") + # New conventions + self.assertIn(DB_QUERY_TEXT, query_span.attributes) + self.assertIn(DB_SYSTEM_NAME, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM_NAME], "sqlite") + + @mock.patch.dict( + "os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "http/dup,database/dup"} + ) + def test_semconv_http_and_database_dup_mode(self): + SQLAlchemyInstrumentor().uninstrument() + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1 + 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + + connect_span = spans[0] + self.assertEqual( + connect_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.25.0", + ) + # Both old and new DB conventions + self.assertIn(DB_SYSTEM, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM], "sqlite") + self.assertIn(DB_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAME], ":memory:") + self.assertIn(DB_SYSTEM_NAME, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_SYSTEM_NAME], "sqlite") + self.assertIn(DB_NAMESPACE, connect_span.attributes) + self.assertEqual(connect_span.attributes[DB_NAMESPACE], ":memory:") + + query_span = spans[1] + # Both old and new DB conventions + self.assertIn(DB_STATEMENT, query_span.attributes) + self.assertIn(DB_SYSTEM, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM], "sqlite") + self.assertIn(DB_QUERY_TEXT, query_span.attributes) + self.assertIn(DB_SYSTEM_NAME, query_span.attributes) + self.assertEqual(query_span.attributes[DB_SYSTEM_NAME], "sqlite") + def test_suppress_instrumentation_cursor_and_metric(self): engine = create_engine("sqlite:///:memory:") SQLAlchemyInstrumentor().instrument( @@ -704,3 +997,145 @@ def test_suppress_instrumentation_cursor_and_metric(self): metric_list = self.get_sorted_metrics() self.assertEqual(len(metric_list), 0) + + def test_get_db_name_from_cursor_or_conn_postgresql_success(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_info = mock.Mock() + mock_info.dbname = "test_database" + mock_connection.info = mock_info + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "postgresql", mock_connection, mock_cursor + ) + self.assertEqual(result, "test_database") + + def test_get_db_name_from_cursor_or_conn_postgresql_no_connection(self): + mock_cursor = mock.Mock() + mock_cursor.connection = None + mock_connection = mock.Mock() + result = _get_db_name_from_cursor_or_conn( + "postgresql", mock_connection, mock_cursor + ) + self.assertIsNone(result) + + def test_get_db_name_from_cursor_or_conn_postgresql_no_dbname(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_info = mock.Mock() + mock_info.dbname = None + mock_connection.info = mock_info + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "postgresql", mock_connection, mock_cursor + ) + self.assertIsNone(result) + + def test_get_db_name_from_cursor_or_conn_unknown_vendor_with_url(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.engine.url.database = "foo" + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "unknown_db", mock_connection, mock_cursor + ) + self.assertEqual(result, "foo") + + def test_get_db_name_from_cursor_or_conn_unknown_vendor_without_url(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.engine.url.database = None + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "unknown_db", mock_connection, mock_cursor + ) + self.assertIsNone(result) + + def test_get_db_name_from_cursor_or_conn_mysql_with_database_attr(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.database = "mysql_test_db" + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "mysql", mock_connection, mock_cursor + ) + self.assertEqual(result, "mysql_test_db") + + def test_get_db_name_from_cursor_or_conn_mysql_with_db_attr_string(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.db = "mysql_test_db" + del mock_connection.database # Remove database attribute + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "mysql", mock_connection, mock_cursor + ) + self.assertEqual(result, "mysql_test_db") + + def test_get_db_name_from_cursor_or_conn_mysql_with_db_attr_bytes(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.db = b"mysql_test_db" + del mock_connection.database # Remove database attribute + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "mysql", mock_connection, mock_cursor + ) + self.assertEqual(result, "mysql_test_db") + + def test_get_db_name_from_cursor_or_conn_mysql_with_cnx_attr(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_cnx = mock.Mock() + mock_cnx.database = "mysql_cnx_db" + mock_cursor.connection = None + mock_cursor._cnx = mock_cnx + result = _get_db_name_from_cursor_or_conn( + "mysql", mock_connection, mock_cursor + ) + self.assertEqual(result, "mysql_cnx_db") + + def test_get_db_name_from_cursor_or_conn_mssql_with_database_attr(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.database = "mssql_test_db" + mock_cursor.connection = mock_connection + result = _get_db_name_from_cursor_or_conn( + "mssql", mock_connection, mock_cursor + ) + self.assertEqual(result, "mssql_test_db") + + def test_get_db_name_from_cursor_or_conn_mysql_variant_names(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.database = "test_db" + mock_cursor.connection = mock_connection + for vendor in ["mysql", "pymysql", "mysqlclient", "mysql+pymysql"]: + result = _get_db_name_from_cursor_or_conn( + vendor, mock_connection, mock_cursor + ) + self.assertEqual(result, "test_db", f"Failed for vendor: {vendor}") + + def test_get_db_name_from_cursor_or_conn_mssql_variant_names(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_connection.database = "test_db" + mock_cursor.connection = mock_connection + for vendor in ["mssql", "mssql+pyodbc", "sqlserver"]: + result = _get_db_name_from_cursor_or_conn( + vendor, mock_connection, mock_cursor + ) + self.assertEqual(result, "test_db", f"Failed for vendor: {vendor}") + + def test_get_db_name_from_cursor_or_conn_postgresql_variant_names(self): + mock_cursor = mock.Mock() + mock_connection = mock.Mock() + mock_info = mock.Mock() + mock_info.dbname = "test_db" + mock_connection.info = mock_info + mock_cursor.connection = mock_connection + for vendor in ["postgresql", "postgres", "postgresql+psycopg2"]: + result = _get_db_name_from_cursor_or_conn( + vendor, mock_connection, mock_cursor + ) + self.assertEqual(result, "test_db", f"Failed for vendor: {vendor}") diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py index 946be53b3c..aeb9f74081 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py @@ -13,6 +13,7 @@ # limitations under the License. import logging import re +from unittest import mock import pytest from sqlalchemy import ( @@ -21,10 +22,17 @@ ) from opentelemetry import context +from opentelemetry.instrumentation._semconv import ( + OTEL_SEMCONV_STABILITY_OPT_IN, + _OpenTelemetrySemanticConventionStability, +) from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor from opentelemetry.semconv._incubating.attributes.db_attributes import ( DB_STATEMENT, ) +from opentelemetry.semconv.attributes.db_attributes import ( + DB_QUERY_TEXT, +) from opentelemetry.test.test_base import TestBase @@ -33,6 +41,10 @@ class TestSqlalchemyInstrumentationWithSQLCommenter(TestBase): def inject_fixtures(self, caplog): self.caplog = caplog # pylint: disable=attribute-defined-outside-init + def setUp(self): + super().setUp() + _OpenTelemetrySemanticConventionStability._initialized = False + def tearDown(self): super().tearDown() SQLAlchemyInstrumentor().uninstrument() @@ -358,6 +370,96 @@ def test_sqlcommenter_enabled_stmt_enabled_create_engine_after_instrumentation( r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", ) + @mock.patch.dict("os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "database"}) + def test_sqlcommenter_enabled_database_mode(self): + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO) + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + enable_commenter=True, + commenter_options={"db_framework": False}, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1;")).fetchall() + # Query log should have sqlcommenter + self.assertRegex( + self.caplog.records[-2].getMessage(), + r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 2) + query_span = spans[1] + # Should use new semconv attribute (db.query.text not db.statement) + self.assertNotIn(DB_STATEMENT, query_span.attributes) + self.assertIn(DB_QUERY_TEXT, query_span.attributes) + self.assertEqual(query_span.attributes[DB_QUERY_TEXT], "SELECT 1;") + + @mock.patch.dict("os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "database"}) + def test_sqlcommenter_enabled_stmt_enabled_database_mode(self): + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO) + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + enable_commenter=True, + commenter_options={"db_framework": False}, + enable_attribute_commenter=True, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1;")).fetchall() + query_log = self.caplog.records[-2].getMessage() + self.assertRegex( + query_log, + r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 2) + query_span = spans[1] + # Should use new semconv attribute with comment + self.assertNotIn(DB_STATEMENT, query_span.attributes) + self.assertIn(DB_QUERY_TEXT, query_span.attributes) + self.assertRegex( + query_span.attributes[DB_QUERY_TEXT], + r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + + @mock.patch.dict( + "os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "database/dup"} + ) + def test_sqlcommenter_enabled_database_dup_mode(self): + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO) + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + enable_commenter=True, + commenter_options={"db_framework": False}, + enable_attribute_commenter=True, + ) + cnx = engine.connect() + cnx.execute(text("SELECT 1;")).fetchall() + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 2) + query_span = spans[1] + # Should have both old and new semconv attributes with comment + self.assertIn(DB_STATEMENT, query_span.attributes) + self.assertRegex( + query_span.attributes[DB_STATEMENT], + r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + self.assertIn(DB_QUERY_TEXT, query_span.attributes) + self.assertRegex( + query_span.attributes[DB_QUERY_TEXT], + r"SELECT 1 /\*db_driver='(.*)',traceparent='\d{1,2}-[a-zA-Z0-9_]{32}-[a-zA-Z0-9_]{16}-\d{1,2}'\*/;", + ) + def test_sqlcommenter_disabled_create_engine_after_instrumentation(self): SQLAlchemyInstrumentor().instrument( tracer_provider=self.tracer_provider, diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/pyproject.toml b/instrumentation/opentelemetry-instrumentation-sqlite3/pyproject.toml index eb08c3f8b9..f837ac585d 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry SQLite3 instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-dbapi == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-dbapi == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/version.py b/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/version.py index 46b492f255..d21c60d918 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/version.py +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/version.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" _instruments = tuple() diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-sqlite3/test-requirements.txt index c6d1cb28e2..9e13e801d1 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py b/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py index 3d8ba2c4aa..7c19f2a9ab 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py @@ -54,6 +54,8 @@ def validate_spans(self, span_name): spans = self.memory_exporter.get_finished_spans() self.memory_exporter.clear() self.assertEqual(len(spans), 2) + root_span = None + child_span = None for span in spans: if span.name == "rootSpan": root_span = span diff --git a/instrumentation/opentelemetry-instrumentation-starlette/pyproject.toml b/instrumentation/opentelemetry-instrumentation-starlette/pyproject.toml index 96fa61939b..298ea6252a 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-starlette/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Starlette Instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-instrumentation-asgi == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-instrumentation-asgi == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py b/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py +++ b/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.latest.txt b/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.latest.txt index f28876044e..280667dffe 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.latest.txt +++ b/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.latest.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv pip compile --python 3.9 --universal --no-emit-package opentelemetry-api --no-emit-package opentelemetry-semantic-conventions -c dev-requirements.txt instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in -o instrumentation/opentelemetry-instrumentation-starlette/test-requirements.latest.txt +# uv pip compile --python 3.10 --universal --no-emit-package opentelemetry-api --no-emit-package opentelemetry-semantic-conventions -c dev-requirements.txt instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in -o instrumentation/opentelemetry-instrumentation-starlette/test-requirements.latest.txt -e instrumentation/opentelemetry-instrumentation-asgi # via # -r instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in @@ -16,11 +16,7 @@ # -r instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-starlette -anyio==4.5.2 ; python_full_version < '3.9' - # via - # httpx - # starlette -anyio==4.8.0 ; python_full_version >= '3.9' +anyio==4.8.0 # via # httpx # starlette @@ -58,7 +54,7 @@ packaging==24.2 # via # opentelemetry-instrumentation # pytest -pluggy==1.5.0 +pluggy==1.6.0 # via pytest pytest==7.4.4 # via @@ -70,9 +66,7 @@ requests==2.32.3 # -r instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in sniffio==1.3.1 # via anyio -starlette==0.44.0 ; python_full_version < '3.9' - # via opentelemetry-instrumentation-starlette -starlette==0.46.2 ; python_full_version >= '3.9' +starlette==0.46.2 # via opentelemetry-instrumentation-starlette tomli==2.2.1 ; python_full_version < '3.11' # via pytest @@ -82,16 +76,11 @@ typing-extensions==4.12.2 # asgiref # opentelemetry-api # opentelemetry-semantic-conventions - # starlette -urllib3==2.2.3 ; python_full_version < '3.9' - # via requests -urllib3==2.3.0 ; python_full_version >= '3.9' +urllib3==2.3.0 # via requests wrapt==1.14.1 # via opentelemetry-instrumentation -zipp==3.20.2 ; python_full_version < '3.9' - # via importlib-metadata -zipp==3.21.0 ; python_full_version >= '3.9' +zipp==3.21.0 # via importlib-metadata # The following packages were excluded from the output: diff --git a/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.oldest.txt b/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.oldest.txt index 5c367c4032..79dbf0ec76 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.oldest.txt +++ b/instrumentation/opentelemetry-instrumentation-starlette/test-requirements.oldest.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv pip compile --python 3.9 --universal --resolution lowest-direct --no-emit-package opentelemetry-api --no-emit-package opentelemetry-semantic-conventions -c dev-requirements.txt instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in -o instrumentation/opentelemetry-instrumentation-starlette/test-requirements.oldest.txt +# uv pip compile --python 3.10 --universal --resolution lowest-direct --no-emit-package opentelemetry-api --no-emit-package opentelemetry-semantic-conventions -c dev-requirements.txt instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in -o instrumentation/opentelemetry-instrumentation-starlette/test-requirements.oldest.txt -e instrumentation/opentelemetry-instrumentation-asgi # via # -r instrumentation/opentelemetry-instrumentation-starlette/test-requirements.in @@ -29,10 +29,6 @@ charset-normalizer==3.4.1 # via requests colorama==0.4.6 ; sys_platform == 'win32' # via pytest -deprecated==1.2.14 - # via - # opentelemetry-api - # opentelemetry-semantic-conventions exceptiongroup==1.2.2 ; python_full_version < '3.11' # via # anyio @@ -56,7 +52,7 @@ packaging==24.0 # via # opentelemetry-instrumentation # pytest -pluggy==1.5.0 +pluggy==1.6.0 # via pytest pytest==7.4.4 # via @@ -72,16 +68,16 @@ starlette==0.13.8 # via opentelemetry-instrumentation-starlette tomli==2.2.1 ; python_full_version < '3.11' # via pytest -typing-extensions==4.12.2 ; python_full_version < '3.13' +typing-extensions==4.12.2 # via # anyio # asgiref + # opentelemetry-api + # opentelemetry-semantic-conventions urllib3==2.3.0 # via requests wrapt==1.16.0 - # via - # deprecated - # opentelemetry-instrumentation + # via opentelemetry-instrumentation zipp==3.21.0 # via importlib-metadata diff --git a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py index a2a22a8e65..0420df7bc5 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py @@ -624,7 +624,7 @@ def create_starlette_app(): app = applications.Starlette() @app.route("/foobar") - def _(request): + def _foobar(request): return PlainTextResponse( content="hi", headers={ @@ -637,7 +637,7 @@ def _(request): ) @app.websocket_route("/foobar_web") - async def _(websocket: WebSocket) -> None: + async def _foobar_web(websocket: WebSocket) -> None: message = await websocket.receive() if message.get("type") == "websocket.connect": await websocket.send( diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/pyproject.toml b/instrumentation/opentelemetry-instrumentation-system-metrics/pyproject.toml index 207f1a0307..3060eaa03c 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry System Metrics Instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,7 +25,7 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-instrumentation == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", "opentelemetry-api ~= 1.11", "psutil >= 5.9.0, < 8", ] diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt index d9d28bdcc4..d30e775ee1 100644 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-system-metrics/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 psutil==7.0.0 py-cpuinfo==9.0.0 pytest==7.4.4 diff --git a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml index 7f692ade89..9f5818117b 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-threading/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Thread context propagation support for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py +++ b/instrumentation/opentelemetry-instrumentation-threading/src/opentelemetry/instrumentation/threading/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt index b40c591b6d..681da528bc 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-threading/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py index 3a06969b26..e0f1c63fbb 100644 --- a/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py +++ b/instrumentation/opentelemetry-instrumentation-threading/tests/test_threading.py @@ -15,7 +15,7 @@ from __future__ import annotations import threading -from concurrent.futures import ( # pylint: disable=no-name-in-module; TODO #4199 +from concurrent.futures import ( Future, ThreadPoolExecutor, ) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml b/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml index 3dea8189e6..5dfb3744c3 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-tornado/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Tornado instrumentation for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -17,7 +17,6 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,9 +25,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py index 190ee02cc8..934d9667d1 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py @@ -776,7 +776,7 @@ def _start_span(tracer, handler, sem_conv_opt_in_mode) -> _TraceContext: span.set_attributes(custom_attributes) activation = trace.use_span(span, end_on_exit=True) - activation.__enter__() # pylint: disable=E1101 + activation.__enter__() # pylint: disable=unnecessary-dunder-call ctx = _TraceContext(activation, span, token) setattr(handler, _HANDLER_CONTEXT_KEY, ctx) @@ -829,7 +829,7 @@ def _finish_span(tracer, handler, error, sem_conv_opt_in_mode): if len(custom_attributes) > 0: ctx.span.set_attributes(custom_attributes) - ctx.activation.__exit__(*finish_args) # pylint: disable=E1101 + ctx.activation.__exit__(*finish_args) # pylint: disable=unnecessary-dunder-call if ctx.token: context.detach(ctx.token) delattr(handler, _HANDLER_CONTEXT_KEY) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/version.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/version.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt index 07cd4106a5..8007e3a539 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-tornado/test-requirements.txt @@ -7,7 +7,7 @@ http_server_mock==1.7 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 requests==2.32.3 diff --git a/instrumentation/opentelemetry-instrumentation-tortoiseorm/pyproject.toml b/instrumentation/opentelemetry-instrumentation-tortoiseorm/pyproject.toml index c6fb4439b7..ee743301b0 100644 --- a/instrumentation/opentelemetry-instrumentation-tortoiseorm/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-tortoiseorm/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Instrumentation for Tortoise ORM" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/version.py b/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/version.py +++ b/instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt index 7d12ae8622..14d714bc5c 100644 --- a/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt @@ -5,7 +5,7 @@ Deprecated==1.2.14 iniconfig==2.0.0 iso8601==1.1.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pydantic==2.12.5 pydantic_core==2.41.5 diff --git a/instrumentation/opentelemetry-instrumentation-urllib/pyproject.toml b/instrumentation/opentelemetry-instrumentation-urllib/pyproject.toml index 6cafe02935..96e9613189 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-urllib/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry urllib instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/version.py b/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/version.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/src/opentelemetry/instrumentation/urllib/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt index 11fc627895..44284f4e63 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 httpretty==1.1.4 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/pyproject.toml b/instrumentation/opentelemetry-instrumentation-urllib3/pyproject.toml index 5ef7097c63..8da041e504 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-urllib3/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry urllib3 instrumentation" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,10 +26,10 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py b/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py index 5c833a4720..0c92b6b6b8 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/__init__.py @@ -424,7 +424,7 @@ def _get_span_name(method: str) -> str: return method -# pylint: disable=too-many-locals +# pylint: disable=too-many-locals,too-many-positional-arguments def _instrument( tracer: Tracer, duration_histogram_old: Histogram, diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/version.py b/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/version.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/src/opentelemetry/instrumentation/urllib3/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt index d85ac17f4f..f2f654f42a 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 httpretty==1.1.4 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt index 14da384548..693b6d6db5 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt @@ -3,7 +3,7 @@ Deprecated==1.2.14 httpretty==1.1.4 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/pyproject.toml b/instrumentation/opentelemetry-instrumentation-wsgi/pyproject.toml index a63f5eee26..c008090b63 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/pyproject.toml +++ b/instrumentation/opentelemetry-instrumentation-wsgi/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "WSGI Middleware for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,9 +26,9 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "opentelemetry-util-http == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "opentelemetry-util-http == 0.63b0.dev", ] [project.optional-dependencies] diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/version.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/version.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-wsgi/test-requirements.txt index 433d9e3201..2aba56695d 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-wsgi/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/opamp-gen-requirements.txt b/opamp-gen-requirements.txt new file mode 100644 index 0000000000..3cd7e79a44 --- /dev/null +++ b/opamp-gen-requirements.txt @@ -0,0 +1,5 @@ +# Use caution when bumping this version to ensure compatibility with the currently supported protobuf version. +# Pinning this to the oldest grpcio version that supports protobuf 5 helps avoid RuntimeWarning messages +# from the generated protobuf code and ensures continued stability for newer grpcio versions. +grpcio-tools==1.63.2 +mypy-protobuf~=3.5.0 diff --git a/opamp/opentelemetry-opamp-client/CHANGELOG.md b/opamp/opentelemetry-opamp-client/CHANGELOG.md new file mode 100644 index 0000000000..d31499d2f4 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +## Version 0.2b0 (2026-04-01) + +- Breaking change: callback class `Callbacks` renamed to `OpAMPCallbacks` + ([#4355](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4355)) + +## Version 0.1b0 (2026-03-23) + +- Initial implementation + ([#3635](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3635)) +- Update client to have additional callback methods + ([#4322](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4322)) diff --git a/instrumentation/opentelemetry-instrumentation-boto/LICENSE b/opamp/opentelemetry-opamp-client/LICENSE similarity index 100% rename from instrumentation/opentelemetry-instrumentation-boto/LICENSE rename to opamp/opentelemetry-opamp-client/LICENSE diff --git a/opamp/opentelemetry-opamp-client/README.rst b/opamp/opentelemetry-opamp-client/README.rst new file mode 100644 index 0000000000..32aef2c5e3 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/README.rst @@ -0,0 +1,24 @@ +OpenTelemetry OpAMP Client +========================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-opamp-client.svg + :target: https://pypi.org/project/opentelemetry-opamp-client/ + +Installation +------------ + +:: + + pip install opentelemetry-opamp-client + + +This package provides an HTTP OpAMP client than can be used by OpenTelemetry distributions to provide remote config. + +References +---------- +* `OpenTelemetry OpAMP Client `_ +* `OpenTelemetry Project `_ +* `OpenTelemetry Python Examples `_ + diff --git a/instrumentation/opentelemetry-instrumentation-boto/pyproject.toml b/opamp/opentelemetry-opamp-client/pyproject.toml similarity index 64% rename from instrumentation/opentelemetry-instrumentation-boto/pyproject.toml rename to opamp/opentelemetry-opamp-client/pyproject.toml index c3c20bf8ea..4d223f77ed 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/pyproject.toml +++ b/opamp/opentelemetry-opamp-client/pyproject.toml @@ -3,12 +3,12 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "opentelemetry-instrumentation-boto" +name = "opentelemetry-opamp-client" dynamic = ["version"] -description = "OpenTelemetry Boto instrumentation" +description = "OpenTelemetry OpAMP client" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,32 +18,24 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", - "opentelemetry-semantic-conventions == 0.62b0.dev", + "protobuf>=5.0, < 7.0", + "uuid-utils>=0.11.0, <1" ] -[project.optional-dependencies] -instruments = [ - "boto~=2.0", -] - -[project.entry-points.opentelemetry_instrumentor] -boto = "opentelemetry.instrumentation.boto:BotoInstrumentor" - [project.urls] -Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-boto" +Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opamp/opentelemetry-opamp-client" Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib" [tool.hatch.version] -path = "src/opentelemetry/instrumentation/boto/version.py" +path = "src/opentelemetry/_opamp/version.py" [tool.hatch.build.targets.sdist] include = [ diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/__init__.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/__init__.py new file mode 100644 index 0000000000..0a9cefcd1f --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/__init__.py @@ -0,0 +1,99 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +OpenTelemetry Python - OpAMP client +----------------------------------- + +This package provides a bunch of classes that can be used by OpenTelemetry distributions implementers +to implement remote config support via the `OpAMP protocol`_. + +The client implements the following capabilities: + +* ReportsStatus +* ReportsHeartbeat +* AcceptsRemoteConfig +* ReportsRemoteConfig +* ReportsEffectiveConfig + +These capabilities are enough to get a remote config from an OpAMP server, parse it, apply it and ack it. + +While the client supports pluggable transports, only an HTTP backends using the ``requests`` library is +implemented. Adding WebSocket support shouldn't be hard but it will require some rework in the OpAMPAgent +class. + +Since OpAMP APIs, config options or environment variables are not standardizes the distros are required +to provide code doing so. +OTel Python distros would need to provide their own OpAMPCallbacks subclass that implements the actual +change of whatever configuration their backends sends. + +Please note that the API is not finalized yet and so the name is called ``_opamp`` with the underscore. + +Usage +----- + +.. code-block:: python + + import os + + from opentelemetry._opamp.agent import OpAMPAgent + from opentelemetry._opamp.callbacks import OpAMPCallbacks + from opentelemetry._opamp.client import OpAMPClient + from opentelemetry.sdk._configuration import _OTelSDKConfigurator + from opentelemetry.sdk.resources import OTELResourceDetector + + + class MyCallbacks(OpAMPCallbacks): + def on_message(self, agent, client, message): + if message.remote_config is None: + return + for config_filename, config in message.remote_config.config.config_map.items(): + print("do something") + + + class MyOpenTelemetryConfigurator(_OTelSDKConfigurator): + def _configure(self, **kwargs): + super()._configure(**kwargs) + + enable_opamp = False + endpoint = os.environ.get("OTEL_PYTHON_OPAMP_ENDPOINT") + if endpoint: + # this is not great but we don't have the calculated resource attributes around + # see https://github.com/open-telemetry/opentelemetry-python/pull/4646 for creating + # an entry point distros can implement + resource = OTELResourceDetector().detect() + agent_identifying_attributes = { + "service.name": resource.attributes.get("service.name"), + } + opamp_client = OpAMPClient( + endpoint=endpoint, + agent_identifying_attributes=agent_identifying_attributes, + ) + opamp_agent = OpAMPAgent( + interval=30, + callbacks=MyCallbacks(), + client=opamp_client, + ) + opamp_agent.start() + +API +--- +.. _OpAMP protocol: https://opentelemetry.io/docs/specs/opamp/ +""" + +from opentelemetry._opamp.agent import OpAMPAgent +from opentelemetry._opamp.callbacks import MessageData, OpAMPCallbacks +from opentelemetry._opamp.client import OpAMPClient + +__all__ = ["MessageData", "OpAMPAgent", "OpAMPCallbacks", "OpAMPClient"] diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/agent.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/agent.py new file mode 100644 index 0000000000..917f753a4f --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/agent.py @@ -0,0 +1,304 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import atexit +import logging +import queue +import random +import threading +from typing import Any, Callable + +from opentelemetry._opamp.callbacks import MessageData, OpAMPCallbacks +from opentelemetry._opamp.client import OpAMPClient +from opentelemetry._opamp.proto import opamp_pb2 + +logger = logging.getLogger(__name__) + + +def _safe_invoke(function: Callable[..., Any], *args: Any) -> None: + function_name = "" + try: + function_name = function.__name__ + function(*args) + except Exception as exc: # pylint: disable=broad-exception-caught + logger.error( + "Error when invoking function '%s'", function_name, exc_info=exc + ) + + +class _Job: + """ + Represents a single request job, with retry/backoff metadata. + """ + + def __init__( + self, + payload: Any, + max_retries: int = 1, + initial_backoff: float = 1.0, + callback: Callable[..., None] | None = None, + ): + self.payload = payload + self.attempt = 0 + self.max_retries = max_retries + self.initial_backoff = initial_backoff + # callback is called after OpAMP message handler is executed + self.callback = callback + + def should_retry(self) -> bool: + """Checks if we should retry again""" + return self.attempt <= self.max_retries + + def delay(self) -> float: + """Calculate the delay before next retry""" + assert self.attempt > 0 + return ( + self.initial_backoff + * (2 ** (self.attempt - 1)) + * random.uniform(0.8, 1.2) + ) + + +class OpAMPAgent: + """ + OpAMPAgent handles: + - periodic “heartbeat” calls enqueued at a fixed interval + - on-demand calls via send() + - exponential backoff retry on failures + - immediate cancellation of all jobs on shutdown + """ + + def __init__( + self, + *, + interval: float = 30, + callbacks: OpAMPCallbacks, + max_retries: int = 10, + heartbeat_max_retries: int = 1, + initial_backoff: float = 1.0, + client: OpAMPClient, + ): + """ + :param interval: seconds between heartbeat calls + :param callbacks: OpAMPCallbacks instance for receiving client events + :param max_retries: how many times to retry a failed job for ad-hoc messages + :param heartbeat_max_retries: how many times to retry an heartbeat failed job + :param initial_backoff: base seconds for exponential backoff + :param client: an OpAMPClient instance + """ + self._interval = interval + self._callbacks = callbacks + self._max_retries = max_retries + self._heartbeat_max_retries = heartbeat_max_retries + self._initial_backoff = initial_backoff + + self._queue: queue.Queue[_Job] = queue.Queue() + self._stop = threading.Event() + + self._worker = threading.Thread( + name="OpAMPAgentWorker", target=self._run_worker, daemon=True + ) + self._scheduler = threading.Thread( + name="OpAMPAgentScheduler", target=self._run_scheduler, daemon=True + ) + # start scheduling only after connection with server has been established + self._schedule = False + + self._client = client + + def _enable_scheduler(self): + self._schedule = True + logger.debug("Connected with endpoint, enabling heartbeat") + + def start(self) -> None: + """ + Starts the scheduler and worker threads. + """ + self._stop.clear() + self._worker.start() + self._scheduler.start() + + atexit.register(self.stop) + + # enqueue the connection message so we can then enable heartbeat + payload = self._client.build_full_state_message() + self.send( + payload, + max_retries=self._max_retries, + callback=self._enable_scheduler, + ) + + def send( + self, + payload: Any, + max_retries: int | None = None, + callback: Callable[..., None] | None = None, + ) -> None: + """ + Enqueue an on-demand request. + """ + if not self._worker.is_alive(): + logger.warning( + "Called send() but worker thread is not alive. Worker threads is started with start()" + ) + + if max_retries is None: + max_retries = self._max_retries + job = _Job( + payload, + max_retries=max_retries, + initial_backoff=self._initial_backoff, + callback=callback, + ) + self._queue.put(job) + logger.debug("On-demand job enqueued: %r", payload) + + def _run_scheduler(self) -> None: + """ + After we made a connection, periodically enqueue “heartbeat” jobs until stop is signaled. + """ + while not self._stop.wait(self._interval): + if self._schedule: + payload = self._client.build_heartbeat_message() + job = _Job( + payload=payload, + max_retries=self._heartbeat_max_retries, + initial_backoff=self._initial_backoff, + ) + self._queue.put(job) + logger.debug("Periodic job enqueued") + + def _run_worker(self) -> None: + """ + Worker loop: pull jobs, attempt the message handler, retry on failure with backoff. + """ + # pylint: disable=broad-exception-caught + while not self._stop.is_set(): + try: + job: _Job = self._queue.get(timeout=1) + except queue.Empty: + continue + + message = None + while job.should_retry() and not self._stop.is_set(): + try: + message = self._client.send(job.payload) + _safe_invoke( + self._callbacks.on_connect, self, self._client + ) + logger.debug("Job succeeded: %r", job.payload) + break + except Exception as exc: + job.attempt += 1 + _safe_invoke( + self._callbacks.on_connect_failed, + self, + self._client, + exc, + ) + logger.warning( + "Job %r failed attempt %d/%d: %s", + job.payload, + job.attempt, + job.max_retries + 1, + exc, + ) + + if not job.should_retry(): + logger.error( + "Job %r dropped after max retries", job.payload + ) + break + + # exponential backoff with +/- 20% jitter, interruptible by stop event + delay = job.delay() + logger.debug("Retrying in %.1fs", delay) + if self._stop.wait(delay): + # stop requested during backoff: abandon job + logger.debug( + "Stop signaled, abandoning job %r", job.payload + ) + break + + if message is not None: + self._process_message(message) + + try: + if job.callback is not None: + job.callback() + except Exception as exc: + logging.warning("Callback for job failed: %s", exc) + finally: + self._queue.task_done() + + def _process_message(self, message: opamp_pb2.ServerToAgent) -> None: + if message.HasField("error_response"): + _safe_invoke( + self._callbacks.on_error, + self, + self._client, + message.error_response, + ) + return + + if message.flags & opamp_pb2.ServerToAgentFlags_ReportFullState: + logger.debug("Server requested full state report") + payload = self._client.build_full_state_message() + self.send(payload) + + msg_data = MessageData.from_server_message(message) + _safe_invoke( + self._callbacks.on_message, + self, + self._client, + msg_data, + ) + + def stop(self, timeout: float | None = None) -> None: + """ + Signal server we are disconnecting and then threads to exit + + :param timeout: seconds to wait for each thread to join + """ + + # Before exiting send signal the server we are disconnecting to free our resources + # This is not required by the spec but is helpful in practice + logger.debug("Stopping OpAMPAgent: sending AgentDisconnect") + payload = self._client.build_agent_disconnect_message() + try: + self._client.send(payload) + except Exception: # pylint: disable=broad-exception-caught + logger.debug( + "Stopping OpAMPAgent: failed to send AgentDisconnect message" + ) + + logger.debug("Stopping OpAMPAgent: signaling threads") + # Signal threads to exit + self._stop.set() + # don't crash if the user calls stop() before start() or calls stop() multiple times + try: + self._worker.join(timeout=timeout) + except RuntimeError as exc: + logger.warning( + "Stopping OpAMPAgent: worker thread failed to join %s", exc + ) + try: + self._scheduler.join(timeout=timeout) + except RuntimeError as exc: + logger.warning( + "Stopping OpAMPAgent: scheduler thread failed to join %s", exc + ) + logger.debug("OpAMPAgent stopped") diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/callbacks.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/callbacks.py new file mode 100644 index 0000000000..c543793ded --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/callbacks.py @@ -0,0 +1,94 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from abc import ABC +from dataclasses import dataclass +from typing import TYPE_CHECKING + +from opentelemetry._opamp.proto import opamp_pb2 + +if TYPE_CHECKING: + from opentelemetry._opamp.agent import OpAMPAgent + from opentelemetry._opamp.client import OpAMPClient + + +@dataclass +class MessageData: + """Structured view of a ServerToAgent message for callback consumption. + + Only fields the agent is expected to act on are exposed. Flags and + error_response are handled internally by the client before this + object reaches the callback. + """ + + remote_config: opamp_pb2.AgentRemoteConfig | None = None + + @classmethod + def from_server_message( + cls, message: opamp_pb2.ServerToAgent + ) -> MessageData: + return cls( + remote_config=message.remote_config + if message.HasField("remote_config") + else None, + ) + + +class OpAMPCallbacks(ABC): + """OpAMP client callbacks with no-op defaults. + + All methods have no-op defaults so that subclasses only need to + override the callbacks they care about. New callbacks can be added + in the future without breaking existing subclasses. + """ + + def on_connect(self, agent: OpAMPAgent, client: OpAMPClient) -> None: + """Called when the connection is successfully established to the + Server. For HTTP clients this is called for any request if the + response status is OK. + """ + + def on_connect_failed( + self, + agent: OpAMPAgent, + client: OpAMPClient, + error: Exception, + ) -> None: + """Called when the connection to the Server cannot be established. + May also be called if the connection is lost and reconnection + attempt fails. + """ + + def on_error( + self, + agent: OpAMPAgent, + client: OpAMPClient, + error_response: opamp_pb2.ServerErrorResponse, + ) -> None: + """Called when the Server reports an error in response to a + previously sent request. Useful for logging purposes. The Agent + should not attempt to process the error by reconnecting or + retrying previous operations. The client handles the UNAVAILABLE + case internally by performing retries as necessary. + """ + + def on_message( + self, + agent: OpAMPAgent, + client: OpAMPClient, + message: MessageData, + ) -> None: + """Called when the Agent receives a message that needs processing.""" diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/client.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/client.py new file mode 100644 index 0000000000..234e15451e --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/client.py @@ -0,0 +1,197 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from logging import getLogger +from typing import Generator, Mapping + +from uuid_utils import uuid7 + +from opentelemetry._opamp import messages +from opentelemetry._opamp.proto import opamp_pb2 +from opentelemetry._opamp.transport.base import HttpTransport +from opentelemetry._opamp.transport.requests import RequestsTransport +from opentelemetry._opamp.version import __version__ +from opentelemetry.context import ( + _SUPPRESS_INSTRUMENTATION_KEY, + attach, + detach, + set_value, +) +from opentelemetry.util.types import AnyValue + +_logger = getLogger(__name__) + +_DEFAULT_OPAMP_TIMEOUT_MS = 1_000 + +_OPAMP_HTTP_HEADERS = { + "Content-Type": "application/x-protobuf", + "User-Agent": "OTel-OpAMP-Python/" + __version__, +} + +_HANDLED_CAPABILITIES = ( + opamp_pb2.AgentCapabilities.AgentCapabilities_ReportsStatus + | opamp_pb2.AgentCapabilities.AgentCapabilities_ReportsHeartbeat + | opamp_pb2.AgentCapabilities.AgentCapabilities_AcceptsRemoteConfig + | opamp_pb2.AgentCapabilities.AgentCapabilities_ReportsRemoteConfig + | opamp_pb2.AgentCapabilities.AgentCapabilities_ReportsEffectiveConfig +) + + +class OpAMPClient: + """ + OpAMPClient implement the helpers for building and sending messages that the agent will use. + """ + + def __init__( + self, + *, + endpoint: str, + headers: Mapping[str, str] | None = None, + timeout_millis: int = _DEFAULT_OPAMP_TIMEOUT_MS, + agent_identifying_attributes: Mapping[str, AnyValue], + agent_non_identifying_attributes: Mapping[str, AnyValue] | None = None, + transport: HttpTransport | None = None, + # this matches requests but can be mapped to other http libraries APIs + tls_certificate: str | bool = True, + tls_client_certificate: str | None = None, + tls_client_key: str | None = None, + ): + self._timeout_millis = timeout_millis + self._transport = ( + RequestsTransport() if transport is None else transport + ) + + self._endpoint = endpoint + headers = headers or {} + self._headers = {**_OPAMP_HTTP_HEADERS, **headers} + self._tls_certificate = tls_certificate + self._tls_client_certificate = tls_client_certificate + self._tls_client_key = tls_client_key + + self._agent_description = messages.build_agent_description( + identifying_attributes=agent_identifying_attributes, + non_identifying_attributes=agent_non_identifying_attributes, + ) + self._sequence_num: int = 0 + self._instance_uid: bytes = uuid7().bytes + self._remote_config_status: opamp_pb2.RemoteConfigStatus | None = None + self._effective_config: opamp_pb2.EffectiveConfig | None = None + + def build_agent_disconnect_message(self) -> bytes: + message = messages.build_agent_disconnect_message( + instance_uid=self._instance_uid, + sequence_num=self._sequence_num, + capabilities=_HANDLED_CAPABILITIES, + ) + data = messages.encode_message(message) + return data + + def build_heartbeat_message(self) -> bytes: + message = messages.build_heartbeat_message( + instance_uid=self._instance_uid, + sequence_num=self._sequence_num, + capabilities=_HANDLED_CAPABILITIES, + ) + data = messages.encode_message(message) + return data + + def update_effective_config( + self, effective_config: dict[str, dict[str, str]], content_type: str + ) -> opamp_pb2.EffectiveConfig: + self._effective_config = messages.build_effective_config_message( + config=effective_config, content_type=content_type + ) + return self._effective_config + + def update_remote_config_status( + self, + remote_config_hash: bytes, + status: opamp_pb2.RemoteConfigStatuses.ValueType, + error_message: str = "", + ) -> opamp_pb2.RemoteConfigStatus | None: + status_changed = ( + not self._remote_config_status + or self._remote_config_status.last_remote_config_hash + != remote_config_hash + or self._remote_config_status.status != status + or self._remote_config_status.error_message != error_message + ) + # if the status changed update we return the RemoteConfigStatus message so that we can send it to the server + if status_changed: + _logger.debug( + "Update remote config status changed for %s", + remote_config_hash, + ) + self._remote_config_status = ( + messages.build_remote_config_status_message( + last_remote_config_hash=remote_config_hash, + status=status, + error_message=error_message, + ) + ) + return self._remote_config_status + + return None + + def build_remote_config_status_response_message( + self, remote_config_status: opamp_pb2.RemoteConfigStatus + ) -> bytes: + message = messages.build_remote_config_status_response_message( + instance_uid=self._instance_uid, + sequence_num=self._sequence_num, + capabilities=_HANDLED_CAPABILITIES, + remote_config_status=remote_config_status, + ) + data = messages.encode_message(message) + return data + + def build_full_state_message(self) -> bytes: + message = messages.build_full_state_message( + instance_uid=self._instance_uid, + agent_description=self._agent_description, + remote_config_status=self._remote_config_status, + sequence_num=self._sequence_num, + effective_config=self._effective_config, + capabilities=_HANDLED_CAPABILITIES, + ) + data = messages.encode_message(message) + return data + + def send(self, data: bytes): + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + try: + response = self._transport.send( + url=self._endpoint, + headers=self._headers, + data=data, + timeout_millis=self._timeout_millis, + tls_certificate=self._tls_certificate, + tls_client_certificate=self._tls_client_certificate, + tls_client_key=self._tls_client_key, + ) + return response + finally: + self._sequence_num += 1 + detach(token) + + @staticmethod + def decode_remote_config( + remote_config: opamp_pb2.AgentRemoteConfig, + ) -> Generator[tuple[str, Mapping[str, AnyValue]]]: + for config_file, config in messages.decode_remote_config( + remote_config + ): + yield config_file, config diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/exceptions.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/exceptions.py new file mode 100644 index 0000000000..2f573b0a55 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/exceptions.py @@ -0,0 +1,25 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class OpAMPTimeoutError(Exception): + pass + + +class OpAMPRemoteConfigParseException(Exception): + pass + + +class OpAMPRemoteConfigDecodeException(Exception): + pass diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/messages.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/messages.py new file mode 100644 index 0000000000..04d4da0322 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/messages.py @@ -0,0 +1,189 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=no-name-in-module + +from __future__ import annotations + +import json +from typing import Generator, Mapping + +from opentelemetry._opamp.exceptions import ( + OpAMPRemoteConfigDecodeException, + OpAMPRemoteConfigParseException, +) +from opentelemetry._opamp.proto import opamp_pb2 +from opentelemetry._opamp.proto.anyvalue_pb2 import ( + AnyValue as PB2AnyValue, +) +from opentelemetry._opamp.proto.anyvalue_pb2 import ( + KeyValue as PB2KeyValue, +) +from opentelemetry.util.types import AnyValue + + +def decode_message(data: bytes) -> opamp_pb2.ServerToAgent: + message = opamp_pb2.ServerToAgent() + message.ParseFromString(data) + return message + + +def _encode_value(value: AnyValue) -> PB2AnyValue: + if value is None: + return PB2AnyValue() + if isinstance(value, bool): + return PB2AnyValue(bool_value=value) + if isinstance(value, int): + return PB2AnyValue(int_value=value) + if isinstance(value, float): + return PB2AnyValue(double_value=value) + if isinstance(value, str): + return PB2AnyValue(string_value=value) + if isinstance(value, bytes): + return PB2AnyValue(bytes_value=value) + # TODO: handle sequence and mapping? + raise ValueError(f"Invalid type {type(value)} of value {value}") + + +def _encode_attributes(attributes: Mapping[str, AnyValue]): + return [ + PB2KeyValue(key=key, value=_encode_value(value)) + for key, value in attributes.items() + ] + + +def build_agent_description( + identifying_attributes: Mapping[str, AnyValue], + non_identifying_attributes: Mapping[str, AnyValue] | None = None, +) -> opamp_pb2.AgentDescription: + identifying_attrs = _encode_attributes(identifying_attributes) + non_identifying_attrs = ( + _encode_attributes(non_identifying_attributes) + if non_identifying_attributes + else None + ) + return opamp_pb2.AgentDescription( + identifying_attributes=identifying_attrs, + non_identifying_attributes=non_identifying_attrs, + ) + + +def build_heartbeat_message( + instance_uid: bytes, sequence_num: int, capabilities: int +) -> opamp_pb2.AgentToServer: + command = opamp_pb2.AgentToServer( + instance_uid=instance_uid, + sequence_num=sequence_num, + capabilities=capabilities, + ) + return command + + +def build_agent_disconnect_message( + instance_uid: bytes, sequence_num: int, capabilities: int +) -> opamp_pb2.AgentToServer: + command = opamp_pb2.AgentToServer( + instance_uid=instance_uid, + sequence_num=sequence_num, + agent_disconnect=opamp_pb2.AgentDisconnect(), + capabilities=capabilities, + ) + return command + + +def build_remote_config_status_message( + last_remote_config_hash: bytes, + status: opamp_pb2.RemoteConfigStatuses.ValueType, + error_message: str = "", +) -> opamp_pb2.RemoteConfigStatus: + return opamp_pb2.RemoteConfigStatus( + last_remote_config_hash=last_remote_config_hash, + status=status, + error_message=error_message, + ) + + +def build_remote_config_status_response_message( + instance_uid: bytes, + sequence_num: int, + capabilities: int, + remote_config_status: opamp_pb2.RemoteConfigStatus, +) -> opamp_pb2.AgentToServer: + command = opamp_pb2.AgentToServer( + instance_uid=instance_uid, + sequence_num=sequence_num, + remote_config_status=remote_config_status, + capabilities=capabilities, + ) + return command + + +def build_effective_config_message( + config: dict[str, dict[str, str]], content_type: str +): + agent_config_map = opamp_pb2.AgentConfigMap() + for filename, value in config.items(): + if content_type == "application/json": + body = json.dumps(value) + agent_config_map.config_map[filename].body = body.encode("utf-8") + agent_config_map.config_map[filename].content_type = content_type + return opamp_pb2.EffectiveConfig( + config_map=agent_config_map, + ) + + +def build_full_state_message( + instance_uid: bytes, + sequence_num: int, + agent_description: opamp_pb2.AgentDescription, + capabilities: int, + remote_config_status: opamp_pb2.RemoteConfigStatus | None, + effective_config: opamp_pb2.EffectiveConfig | None, +) -> opamp_pb2.AgentToServer: + command = opamp_pb2.AgentToServer( + instance_uid=instance_uid, + sequence_num=sequence_num, + agent_description=agent_description, + remote_config_status=remote_config_status, + effective_config=effective_config, + capabilities=capabilities, + ) + return command + + +def encode_message(data: opamp_pb2.AgentToServer) -> bytes: + return data.SerializeToString() + + +def decode_remote_config( + remote_config: opamp_pb2.AgentRemoteConfig, +) -> Generator[tuple[str, Mapping[str, AnyValue]]]: + for ( + config_file_name, + config_file, + ) in remote_config.config.config_map.items(): + if config_file.content_type in ("application/json", "text/json"): + try: + body = config_file.body.decode() + config_data = json.loads(body) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise OpAMPRemoteConfigDecodeException( + f"Failed to decode {config_file_name} with content type {config_file.content_type}: {exc}" + ) + + yield config_file_name, config_data + else: + raise OpAMPRemoteConfigParseException( + f"Cannot parse {config_file_name} with content type {config_file.content_type}" + ) diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/anyvalue_pb2.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/anyvalue_pb2.py new file mode 100644 index 0000000000..7d1cd9b5b6 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/anyvalue_pb2.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: anyvalue.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x61nyvalue.proto\x12\x0bopamp.proto\"\xe8\x01\n\x08\x41nyValue\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x14\n\nbool_value\x18\x02 \x01(\x08H\x00\x12\x13\n\tint_value\x18\x03 \x01(\x03H\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12.\n\x0b\x61rray_value\x18\x05 \x01(\x0b\x32\x17.opamp.proto.ArrayValueH\x00\x12\x31\n\x0ckvlist_value\x18\x06 \x01(\x0b\x32\x19.opamp.proto.KeyValueListH\x00\x12\x15\n\x0b\x62ytes_value\x18\x07 \x01(\x0cH\x00\x42\x07\n\x05value\"3\n\nArrayValue\x12%\n\x06values\x18\x01 \x03(\x0b\x32\x15.opamp.proto.AnyValue\"5\n\x0cKeyValueList\x12%\n\x06values\x18\x01 \x03(\x0b\x32\x15.opamp.proto.KeyValue\"=\n\x08KeyValue\x12\x0b\n\x03key\x18\x01 \x01(\t\x12$\n\x05value\x18\x02 \x01(\x0b\x32\x15.opamp.proto.AnyValueB.Z,github.com/open-telemetry/opamp-go/protobufsb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'anyvalue_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z,github.com/open-telemetry/opamp-go/protobufs' + _globals['_ANYVALUE']._serialized_start=32 + _globals['_ANYVALUE']._serialized_end=264 + _globals['_ARRAYVALUE']._serialized_start=266 + _globals['_ARRAYVALUE']._serialized_end=317 + _globals['_KEYVALUELIST']._serialized_start=319 + _globals['_KEYVALUELIST']._serialized_end=372 + _globals['_KEYVALUE']._serialized_start=374 + _globals['_KEYVALUE']._serialized_end=435 +# @@protoc_insertion_point(module_scope) diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/anyvalue_pb2.pyi b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/anyvalue_pb2.pyi new file mode 100644 index 0000000000..5036b8eb5a --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/anyvalue_pb2.pyi @@ -0,0 +1,135 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +This file is copied and modified from https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/common/v1/common.proto +Modifications: + - Removal of unneeded InstrumentationLibrary and StringKeyValue messages. + - Change of go_package to reference a package in this repo. + - Removal of gogoproto usage. +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class AnyValue(google.protobuf.message.Message): + """AnyValue is used to represent any type of attribute value. AnyValue may contain a + primitive value such as a string or integer or it may contain an arbitrary nested + object containing arrays, key-value lists and primitives. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STRING_VALUE_FIELD_NUMBER: builtins.int + BOOL_VALUE_FIELD_NUMBER: builtins.int + INT_VALUE_FIELD_NUMBER: builtins.int + DOUBLE_VALUE_FIELD_NUMBER: builtins.int + ARRAY_VALUE_FIELD_NUMBER: builtins.int + KVLIST_VALUE_FIELD_NUMBER: builtins.int + BYTES_VALUE_FIELD_NUMBER: builtins.int + string_value: builtins.str + bool_value: builtins.bool + int_value: builtins.int + double_value: builtins.float + @property + def array_value(self) -> global___ArrayValue: ... + @property + def kvlist_value(self) -> global___KeyValueList: ... + bytes_value: builtins.bytes + def __init__( + self, + *, + string_value: builtins.str = ..., + bool_value: builtins.bool = ..., + int_value: builtins.int = ..., + double_value: builtins.float = ..., + array_value: global___ArrayValue | None = ..., + kvlist_value: global___KeyValueList | None = ..., + bytes_value: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["array_value", b"array_value", "bool_value", b"bool_value", "bytes_value", b"bytes_value", "double_value", b"double_value", "int_value", b"int_value", "kvlist_value", b"kvlist_value", "string_value", b"string_value", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["array_value", b"array_value", "bool_value", b"bool_value", "bytes_value", b"bytes_value", "double_value", b"double_value", "int_value", b"int_value", "kvlist_value", b"kvlist_value", "string_value", b"string_value", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["string_value", "bool_value", "int_value", "double_value", "array_value", "kvlist_value", "bytes_value"] | None: ... + +global___AnyValue = AnyValue + +@typing_extensions.final +class ArrayValue(google.protobuf.message.Message): + """ArrayValue is a list of AnyValue messages. We need ArrayValue as a message + since oneof in AnyValue does not allow repeated fields. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUES_FIELD_NUMBER: builtins.int + @property + def values(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AnyValue]: + """Array of values. The array may be empty (contain 0 elements).""" + def __init__( + self, + *, + values: collections.abc.Iterable[global___AnyValue] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["values", b"values"]) -> None: ... + +global___ArrayValue = ArrayValue + +@typing_extensions.final +class KeyValueList(google.protobuf.message.Message): + """KeyValueList is a list of KeyValue messages. We need KeyValueList as a message + since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need + a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to + avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches + are semantically equivalent. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUES_FIELD_NUMBER: builtins.int + @property + def values(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___KeyValue]: + """A collection of key/value pairs of key-value pairs. The list may be empty (may + contain 0 elements). + """ + def __init__( + self, + *, + values: collections.abc.Iterable[global___KeyValue] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["values", b"values"]) -> None: ... + +global___KeyValueList = KeyValueList + +@typing_extensions.final +class KeyValue(google.protobuf.message.Message): + """KeyValue is a key-value pair that is used to store Span attributes, Link + attributes, etc. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___AnyValue: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___AnyValue | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + +global___KeyValue = KeyValue diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/opamp_pb2.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/opamp_pb2.py new file mode 100644 index 0000000000..00a62682d6 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/opamp_pb2.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: opamp.proto +# Protobuf Python Version: 5.26.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import anyvalue_pb2 as anyvalue__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bopamp.proto\x12\x0bopamp.proto\x1a\x0e\x61nyvalue.proto\"\xae\x05\n\rAgentToServer\x12\x14\n\x0cinstance_uid\x18\x01 \x01(\x0c\x12\x14\n\x0csequence_num\x18\x02 \x01(\x04\x12\x38\n\x11\x61gent_description\x18\x03 \x01(\x0b\x32\x1d.opamp.proto.AgentDescription\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x01(\x04\x12,\n\x06health\x18\x05 \x01(\x0b\x32\x1c.opamp.proto.ComponentHealth\x12\x36\n\x10\x65\x66\x66\x65\x63tive_config\x18\x06 \x01(\x0b\x32\x1c.opamp.proto.EffectiveConfig\x12=\n\x14remote_config_status\x18\x07 \x01(\x0b\x32\x1f.opamp.proto.RemoteConfigStatus\x12\x36\n\x10package_statuses\x18\x08 \x01(\x0b\x32\x1c.opamp.proto.PackageStatuses\x12\x36\n\x10\x61gent_disconnect\x18\t \x01(\x0b\x32\x1c.opamp.proto.AgentDisconnect\x12\r\n\x05\x66lags\x18\n \x01(\x04\x12K\n\x1b\x63onnection_settings_request\x18\x0b \x01(\x0b\x32&.opamp.proto.ConnectionSettingsRequest\x12<\n\x13\x63ustom_capabilities\x18\x0c \x01(\x0b\x32\x1f.opamp.proto.CustomCapabilities\x12\x32\n\x0e\x63ustom_message\x18\r \x01(\x0b\x32\x1a.opamp.proto.CustomMessage\x12>\n\x14\x61vailable_components\x18\x0e \x01(\x0b\x32 .opamp.proto.AvailableComponents\"\x11\n\x0f\x41gentDisconnect\"W\n\x19\x43onnectionSettingsRequest\x12:\n\x05opamp\x18\x01 \x01(\x0b\x32+.opamp.proto.OpAMPConnectionSettingsRequest\"^\n\x1eOpAMPConnectionSettingsRequest\x12<\n\x13\x63\x65rtificate_request\x18\x01 \x01(\x0b\x32\x1f.opamp.proto.CertificateRequest\"!\n\x12\x43\x65rtificateRequest\x12\x0b\n\x03\x63sr\x18\x01 \x01(\x0c\"\xbb\x01\n\x13\x41vailableComponents\x12\x44\n\ncomponents\x18\x01 \x03(\x0b\x32\x30.opamp.proto.AvailableComponents.ComponentsEntry\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\x1aP\n\x0f\x43omponentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x1d.opamp.proto.ComponentDetails:\x02\x38\x01\"\xe1\x01\n\x10\x43omponentDetails\x12\'\n\x08metadata\x18\x01 \x03(\x0b\x32\x15.opamp.proto.KeyValue\x12M\n\x11sub_component_map\x18\x02 \x03(\x0b\x32\x32.opamp.proto.ComponentDetails.SubComponentMapEntry\x1aU\n\x14SubComponentMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x1d.opamp.proto.ComponentDetails:\x02\x38\x01\"\xa1\x04\n\rServerToAgent\x12\x14\n\x0cinstance_uid\x18\x01 \x01(\x0c\x12\x38\n\x0e\x65rror_response\x18\x02 \x01(\x0b\x32 .opamp.proto.ServerErrorResponse\x12\x35\n\rremote_config\x18\x03 \x01(\x0b\x32\x1e.opamp.proto.AgentRemoteConfig\x12\x42\n\x13\x63onnection_settings\x18\x04 \x01(\x0b\x32%.opamp.proto.ConnectionSettingsOffers\x12:\n\x12packages_available\x18\x05 \x01(\x0b\x32\x1e.opamp.proto.PackagesAvailable\x12\r\n\x05\x66lags\x18\x06 \x01(\x04\x12\x14\n\x0c\x63\x61pabilities\x18\x07 \x01(\x04\x12>\n\x14\x61gent_identification\x18\x08 \x01(\x0b\x32 .opamp.proto.AgentIdentification\x12\x32\n\x07\x63ommand\x18\t \x01(\x0b\x32!.opamp.proto.ServerToAgentCommand\x12<\n\x13\x63ustom_capabilities\x18\n \x01(\x0b\x32\x1f.opamp.proto.CustomCapabilities\x12\x32\n\x0e\x63ustom_message\x18\x0b \x01(\x0b\x32\x1a.opamp.proto.CustomMessage\"\xb4\x01\n\x17OpAMPConnectionSettings\x12\x1c\n\x14\x64\x65stination_endpoint\x18\x01 \x01(\t\x12%\n\x07headers\x18\x02 \x01(\x0b\x32\x14.opamp.proto.Headers\x12\x30\n\x0b\x63\x65rtificate\x18\x03 \x01(\x0b\x32\x1b.opamp.proto.TLSCertificate\x12\"\n\x1aheartbeat_interval_seconds\x18\x04 \x01(\x04\"\x94\x01\n\x1bTelemetryConnectionSettings\x12\x1c\n\x14\x64\x65stination_endpoint\x18\x01 \x01(\t\x12%\n\x07headers\x18\x02 \x01(\x0b\x32\x14.opamp.proto.Headers\x12\x30\n\x0b\x63\x65rtificate\x18\x03 \x01(\x0b\x32\x1b.opamp.proto.TLSCertificate\"\x97\x02\n\x17OtherConnectionSettings\x12\x1c\n\x14\x64\x65stination_endpoint\x18\x01 \x01(\t\x12%\n\x07headers\x18\x02 \x01(\x0b\x32\x14.opamp.proto.Headers\x12\x30\n\x0b\x63\x65rtificate\x18\x03 \x01(\x0b\x32\x1b.opamp.proto.TLSCertificate\x12O\n\x0eother_settings\x18\x04 \x03(\x0b\x32\x37.opamp.proto.OtherConnectionSettings.OtherSettingsEntry\x1a\x34\n\x12OtherSettingsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"/\n\x07Headers\x12$\n\x07headers\x18\x01 \x03(\x0b\x32\x13.opamp.proto.Header\"$\n\x06Header\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"D\n\x0eTLSCertificate\x12\x0c\n\x04\x63\x65rt\x18\x01 \x01(\x0c\x12\x13\n\x0bprivate_key\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63\x61_cert\x18\x03 \x01(\x0c\"\xcd\x03\n\x18\x43onnectionSettingsOffers\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\x33\n\x05opamp\x18\x02 \x01(\x0b\x32$.opamp.proto.OpAMPConnectionSettings\x12=\n\x0bown_metrics\x18\x03 \x01(\x0b\x32(.opamp.proto.TelemetryConnectionSettings\x12<\n\nown_traces\x18\x04 \x01(\x0b\x32(.opamp.proto.TelemetryConnectionSettings\x12:\n\x08own_logs\x18\x05 \x01(\x0b\x32(.opamp.proto.TelemetryConnectionSettings\x12V\n\x11other_connections\x18\x06 \x03(\x0b\x32;.opamp.proto.ConnectionSettingsOffers.OtherConnectionsEntry\x1a]\n\x15OtherConnectionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x33\n\x05value\x18\x02 \x01(\x0b\x32$.opamp.proto.OtherConnectionSettings:\x02\x38\x01\"\xbe\x01\n\x11PackagesAvailable\x12>\n\x08packages\x18\x01 \x03(\x0b\x32,.opamp.proto.PackagesAvailable.PackagesEntry\x12\x19\n\x11\x61ll_packages_hash\x18\x02 \x01(\x0c\x1aN\n\rPackagesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x1d.opamp.proto.PackageAvailable:\x02\x38\x01\"\x86\x01\n\x10PackageAvailable\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.opamp.proto.PackageType\x12\x0f\n\x07version\x18\x02 \x01(\t\x12+\n\x04\x66ile\x18\x03 \x01(\x0b\x32\x1d.opamp.proto.DownloadableFile\x12\x0c\n\x04hash\x18\x04 \x01(\x0c\"x\n\x10\x44ownloadableFile\x12\x14\n\x0c\x64ownload_url\x18\x01 \x01(\t\x12\x14\n\x0c\x63ontent_hash\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12%\n\x07headers\x18\x04 \x01(\x0b\x32\x14.opamp.proto.Headers\"\x99\x01\n\x13ServerErrorResponse\x12\x32\n\x04type\x18\x01 \x01(\x0e\x32$.opamp.proto.ServerErrorResponseType\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12,\n\nretry_info\x18\x03 \x01(\x0b\x32\x16.opamp.proto.RetryInfoH\x00\x42\t\n\x07\x44\x65tails\",\n\tRetryInfo\x12\x1f\n\x17retry_after_nanoseconds\x18\x01 \x01(\x04\">\n\x14ServerToAgentCommand\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.opamp.proto.CommandType\"\x84\x01\n\x10\x41gentDescription\x12\x35\n\x16identifying_attributes\x18\x01 \x03(\x0b\x32\x15.opamp.proto.KeyValue\x12\x39\n\x1anon_identifying_attributes\x18\x02 \x03(\x0b\x32\x15.opamp.proto.KeyValue\"\xb0\x02\n\x0f\x43omponentHealth\x12\x0f\n\x07healthy\x18\x01 \x01(\x08\x12\x1c\n\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x12\n\nlast_error\x18\x03 \x01(\t\x12\x0e\n\x06status\x18\x04 \x01(\t\x12\x1d\n\x15status_time_unix_nano\x18\x05 \x01(\x06\x12R\n\x14\x63omponent_health_map\x18\x06 \x03(\x0b\x32\x34.opamp.proto.ComponentHealth.ComponentHealthMapEntry\x1aW\n\x17\x43omponentHealthMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.opamp.proto.ComponentHealth:\x02\x38\x01\"B\n\x0f\x45\x66\x66\x65\x63tiveConfig\x12/\n\nconfig_map\x18\x01 \x01(\x0b\x32\x1b.opamp.proto.AgentConfigMap\"\x7f\n\x12RemoteConfigStatus\x12\x1f\n\x17last_remote_config_hash\x18\x01 \x01(\x0c\x12\x31\n\x06status\x18\x02 \x01(\x0e\x32!.opamp.proto.RemoteConfigStatuses\x12\x15\n\rerror_message\x18\x03 \x01(\t\"\xde\x01\n\x0fPackageStatuses\x12<\n\x08packages\x18\x01 \x03(\x0b\x32*.opamp.proto.PackageStatuses.PackagesEntry\x12)\n!server_provided_all_packages_hash\x18\x02 \x01(\x0c\x12\x15\n\rerror_message\x18\x03 \x01(\t\x1aK\n\rPackagesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12)\n\x05value\x18\x02 \x01(\x0b\x32\x1a.opamp.proto.PackageStatus:\x02\x38\x01\"\x93\x02\n\rPackageStatus\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x11\x61gent_has_version\x18\x02 \x01(\t\x12\x16\n\x0e\x61gent_has_hash\x18\x03 \x01(\x0c\x12\x1e\n\x16server_offered_version\x18\x04 \x01(\t\x12\x1b\n\x13server_offered_hash\x18\x05 \x01(\x0c\x12.\n\x06status\x18\x06 \x01(\x0e\x32\x1e.opamp.proto.PackageStatusEnum\x12\x15\n\rerror_message\x18\x07 \x01(\t\x12=\n\x10\x64ownload_details\x18\x08 \x01(\x0b\x32#.opamp.proto.PackageDownloadDetails\"U\n\x16PackageDownloadDetails\x12\x18\n\x10\x64ownload_percent\x18\x01 \x01(\x01\x12!\n\x19\x64ownload_bytes_per_second\x18\x02 \x01(\x01\"/\n\x13\x41gentIdentification\x12\x18\n\x10new_instance_uid\x18\x01 \x01(\x0c\"U\n\x11\x41gentRemoteConfig\x12+\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x1b.opamp.proto.AgentConfigMap\x12\x13\n\x0b\x63onfig_hash\x18\x02 \x01(\x0c\"\xa0\x01\n\x0e\x41gentConfigMap\x12>\n\nconfig_map\x18\x01 \x03(\x0b\x32*.opamp.proto.AgentConfigMap.ConfigMapEntry\x1aN\n\x0e\x43onfigMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.opamp.proto.AgentConfigFile:\x02\x38\x01\"5\n\x0f\x41gentConfigFile\x12\x0c\n\x04\x62ody\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"*\n\x12\x43ustomCapabilities\x12\x14\n\x0c\x63\x61pabilities\x18\x01 \x03(\t\"?\n\rCustomMessage\x12\x12\n\ncapability\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c*c\n\x12\x41gentToServerFlags\x12\"\n\x1e\x41gentToServerFlags_Unspecified\x10\x00\x12)\n%AgentToServerFlags_RequestInstanceUid\x10\x01*\x92\x01\n\x12ServerToAgentFlags\x12\"\n\x1eServerToAgentFlags_Unspecified\x10\x00\x12&\n\"ServerToAgentFlags_ReportFullState\x10\x01\x12\x30\n,ServerToAgentFlags_ReportAvailableComponents\x10\x02*\xf7\x02\n\x12ServerCapabilities\x12\"\n\x1eServerCapabilities_Unspecified\x10\x00\x12$\n ServerCapabilities_AcceptsStatus\x10\x01\x12)\n%ServerCapabilities_OffersRemoteConfig\x10\x02\x12-\n)ServerCapabilities_AcceptsEffectiveConfig\x10\x04\x12%\n!ServerCapabilities_OffersPackages\x10\x08\x12,\n(ServerCapabilities_AcceptsPackagesStatus\x10\x10\x12/\n+ServerCapabilities_OffersConnectionSettings\x10 \x12\x37\n3ServerCapabilities_AcceptsConnectionSettingsRequest\x10@*>\n\x0bPackageType\x12\x18\n\x14PackageType_TopLevel\x10\x00\x12\x15\n\x11PackageType_Addon\x10\x01*\x8f\x01\n\x17ServerErrorResponseType\x12#\n\x1fServerErrorResponseType_Unknown\x10\x00\x12&\n\"ServerErrorResponseType_BadRequest\x10\x01\x12\'\n#ServerErrorResponseType_Unavailable\x10\x02*&\n\x0b\x43ommandType\x12\x17\n\x13\x43ommandType_Restart\x10\x00*\xcc\x05\n\x11\x41gentCapabilities\x12!\n\x1d\x41gentCapabilities_Unspecified\x10\x00\x12#\n\x1f\x41gentCapabilities_ReportsStatus\x10\x01\x12)\n%AgentCapabilities_AcceptsRemoteConfig\x10\x02\x12,\n(AgentCapabilities_ReportsEffectiveConfig\x10\x04\x12%\n!AgentCapabilities_AcceptsPackages\x10\x08\x12,\n(AgentCapabilities_ReportsPackageStatuses\x10\x10\x12&\n\"AgentCapabilities_ReportsOwnTraces\x10 \x12\'\n#AgentCapabilities_ReportsOwnMetrics\x10@\x12%\n AgentCapabilities_ReportsOwnLogs\x10\x80\x01\x12\x35\n0AgentCapabilities_AcceptsOpAMPConnectionSettings\x10\x80\x02\x12\x35\n0AgentCapabilities_AcceptsOtherConnectionSettings\x10\x80\x04\x12,\n\'AgentCapabilities_AcceptsRestartCommand\x10\x80\x08\x12$\n\x1f\x41gentCapabilities_ReportsHealth\x10\x80\x10\x12*\n%AgentCapabilities_ReportsRemoteConfig\x10\x80 \x12\'\n\"AgentCapabilities_ReportsHeartbeat\x10\x80@\x12\x32\n,AgentCapabilities_ReportsAvailableComponents\x10\x80\x80\x01*\x9c\x01\n\x14RemoteConfigStatuses\x12\x1e\n\x1aRemoteConfigStatuses_UNSET\x10\x00\x12 \n\x1cRemoteConfigStatuses_APPLIED\x10\x01\x12!\n\x1dRemoteConfigStatuses_APPLYING\x10\x02\x12\x1f\n\x1bRemoteConfigStatuses_FAILED\x10\x03*\xc4\x01\n\x11PackageStatusEnum\x12\x1f\n\x1bPackageStatusEnum_Installed\x10\x00\x12$\n PackageStatusEnum_InstallPending\x10\x01\x12 \n\x1cPackageStatusEnum_Installing\x10\x02\x12#\n\x1fPackageStatusEnum_InstallFailed\x10\x03\x12!\n\x1dPackageStatusEnum_Downloading\x10\x04\x42.Z,github.com/open-telemetry/opamp-go/protobufsb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'opamp_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z,github.com/open-telemetry/opamp-go/protobufs' + _globals['_AVAILABLECOMPONENTS_COMPONENTSENTRY']._loaded_options = None + _globals['_AVAILABLECOMPONENTS_COMPONENTSENTRY']._serialized_options = b'8\001' + _globals['_COMPONENTDETAILS_SUBCOMPONENTMAPENTRY']._loaded_options = None + _globals['_COMPONENTDETAILS_SUBCOMPONENTMAPENTRY']._serialized_options = b'8\001' + _globals['_OTHERCONNECTIONSETTINGS_OTHERSETTINGSENTRY']._loaded_options = None + _globals['_OTHERCONNECTIONSETTINGS_OTHERSETTINGSENTRY']._serialized_options = b'8\001' + _globals['_CONNECTIONSETTINGSOFFERS_OTHERCONNECTIONSENTRY']._loaded_options = None + _globals['_CONNECTIONSETTINGSOFFERS_OTHERCONNECTIONSENTRY']._serialized_options = b'8\001' + _globals['_PACKAGESAVAILABLE_PACKAGESENTRY']._loaded_options = None + _globals['_PACKAGESAVAILABLE_PACKAGESENTRY']._serialized_options = b'8\001' + _globals['_COMPONENTHEALTH_COMPONENTHEALTHMAPENTRY']._loaded_options = None + _globals['_COMPONENTHEALTH_COMPONENTHEALTHMAPENTRY']._serialized_options = b'8\001' + _globals['_PACKAGESTATUSES_PACKAGESENTRY']._loaded_options = None + _globals['_PACKAGESTATUSES_PACKAGESENTRY']._serialized_options = b'8\001' + _globals['_AGENTCONFIGMAP_CONFIGMAPENTRY']._loaded_options = None + _globals['_AGENTCONFIGMAP_CONFIGMAPENTRY']._serialized_options = b'8\001' + _globals['_AGENTTOSERVERFLAGS']._serialized_start=5585 + _globals['_AGENTTOSERVERFLAGS']._serialized_end=5684 + _globals['_SERVERTOAGENTFLAGS']._serialized_start=5687 + _globals['_SERVERTOAGENTFLAGS']._serialized_end=5833 + _globals['_SERVERCAPABILITIES']._serialized_start=5836 + _globals['_SERVERCAPABILITIES']._serialized_end=6211 + _globals['_PACKAGETYPE']._serialized_start=6213 + _globals['_PACKAGETYPE']._serialized_end=6275 + _globals['_SERVERERRORRESPONSETYPE']._serialized_start=6278 + _globals['_SERVERERRORRESPONSETYPE']._serialized_end=6421 + _globals['_COMMANDTYPE']._serialized_start=6423 + _globals['_COMMANDTYPE']._serialized_end=6461 + _globals['_AGENTCAPABILITIES']._serialized_start=6464 + _globals['_AGENTCAPABILITIES']._serialized_end=7180 + _globals['_REMOTECONFIGSTATUSES']._serialized_start=7183 + _globals['_REMOTECONFIGSTATUSES']._serialized_end=7339 + _globals['_PACKAGESTATUSENUM']._serialized_start=7342 + _globals['_PACKAGESTATUSENUM']._serialized_end=7538 + _globals['_AGENTTOSERVER']._serialized_start=45 + _globals['_AGENTTOSERVER']._serialized_end=731 + _globals['_AGENTDISCONNECT']._serialized_start=733 + _globals['_AGENTDISCONNECT']._serialized_end=750 + _globals['_CONNECTIONSETTINGSREQUEST']._serialized_start=752 + _globals['_CONNECTIONSETTINGSREQUEST']._serialized_end=839 + _globals['_OPAMPCONNECTIONSETTINGSREQUEST']._serialized_start=841 + _globals['_OPAMPCONNECTIONSETTINGSREQUEST']._serialized_end=935 + _globals['_CERTIFICATEREQUEST']._serialized_start=937 + _globals['_CERTIFICATEREQUEST']._serialized_end=970 + _globals['_AVAILABLECOMPONENTS']._serialized_start=973 + _globals['_AVAILABLECOMPONENTS']._serialized_end=1160 + _globals['_AVAILABLECOMPONENTS_COMPONENTSENTRY']._serialized_start=1080 + _globals['_AVAILABLECOMPONENTS_COMPONENTSENTRY']._serialized_end=1160 + _globals['_COMPONENTDETAILS']._serialized_start=1163 + _globals['_COMPONENTDETAILS']._serialized_end=1388 + _globals['_COMPONENTDETAILS_SUBCOMPONENTMAPENTRY']._serialized_start=1303 + _globals['_COMPONENTDETAILS_SUBCOMPONENTMAPENTRY']._serialized_end=1388 + _globals['_SERVERTOAGENT']._serialized_start=1391 + _globals['_SERVERTOAGENT']._serialized_end=1936 + _globals['_OPAMPCONNECTIONSETTINGS']._serialized_start=1939 + _globals['_OPAMPCONNECTIONSETTINGS']._serialized_end=2119 + _globals['_TELEMETRYCONNECTIONSETTINGS']._serialized_start=2122 + _globals['_TELEMETRYCONNECTIONSETTINGS']._serialized_end=2270 + _globals['_OTHERCONNECTIONSETTINGS']._serialized_start=2273 + _globals['_OTHERCONNECTIONSETTINGS']._serialized_end=2552 + _globals['_OTHERCONNECTIONSETTINGS_OTHERSETTINGSENTRY']._serialized_start=2500 + _globals['_OTHERCONNECTIONSETTINGS_OTHERSETTINGSENTRY']._serialized_end=2552 + _globals['_HEADERS']._serialized_start=2554 + _globals['_HEADERS']._serialized_end=2601 + _globals['_HEADER']._serialized_start=2603 + _globals['_HEADER']._serialized_end=2639 + _globals['_TLSCERTIFICATE']._serialized_start=2641 + _globals['_TLSCERTIFICATE']._serialized_end=2709 + _globals['_CONNECTIONSETTINGSOFFERS']._serialized_start=2712 + _globals['_CONNECTIONSETTINGSOFFERS']._serialized_end=3173 + _globals['_CONNECTIONSETTINGSOFFERS_OTHERCONNECTIONSENTRY']._serialized_start=3080 + _globals['_CONNECTIONSETTINGSOFFERS_OTHERCONNECTIONSENTRY']._serialized_end=3173 + _globals['_PACKAGESAVAILABLE']._serialized_start=3176 + _globals['_PACKAGESAVAILABLE']._serialized_end=3366 + _globals['_PACKAGESAVAILABLE_PACKAGESENTRY']._serialized_start=3288 + _globals['_PACKAGESAVAILABLE_PACKAGESENTRY']._serialized_end=3366 + _globals['_PACKAGEAVAILABLE']._serialized_start=3369 + _globals['_PACKAGEAVAILABLE']._serialized_end=3503 + _globals['_DOWNLOADABLEFILE']._serialized_start=3505 + _globals['_DOWNLOADABLEFILE']._serialized_end=3625 + _globals['_SERVERERRORRESPONSE']._serialized_start=3628 + _globals['_SERVERERRORRESPONSE']._serialized_end=3781 + _globals['_RETRYINFO']._serialized_start=3783 + _globals['_RETRYINFO']._serialized_end=3827 + _globals['_SERVERTOAGENTCOMMAND']._serialized_start=3829 + _globals['_SERVERTOAGENTCOMMAND']._serialized_end=3891 + _globals['_AGENTDESCRIPTION']._serialized_start=3894 + _globals['_AGENTDESCRIPTION']._serialized_end=4026 + _globals['_COMPONENTHEALTH']._serialized_start=4029 + _globals['_COMPONENTHEALTH']._serialized_end=4333 + _globals['_COMPONENTHEALTH_COMPONENTHEALTHMAPENTRY']._serialized_start=4246 + _globals['_COMPONENTHEALTH_COMPONENTHEALTHMAPENTRY']._serialized_end=4333 + _globals['_EFFECTIVECONFIG']._serialized_start=4335 + _globals['_EFFECTIVECONFIG']._serialized_end=4401 + _globals['_REMOTECONFIGSTATUS']._serialized_start=4403 + _globals['_REMOTECONFIGSTATUS']._serialized_end=4530 + _globals['_PACKAGESTATUSES']._serialized_start=4533 + _globals['_PACKAGESTATUSES']._serialized_end=4755 + _globals['_PACKAGESTATUSES_PACKAGESENTRY']._serialized_start=4680 + _globals['_PACKAGESTATUSES_PACKAGESENTRY']._serialized_end=4755 + _globals['_PACKAGESTATUS']._serialized_start=4758 + _globals['_PACKAGESTATUS']._serialized_end=5033 + _globals['_PACKAGEDOWNLOADDETAILS']._serialized_start=5035 + _globals['_PACKAGEDOWNLOADDETAILS']._serialized_end=5120 + _globals['_AGENTIDENTIFICATION']._serialized_start=5122 + _globals['_AGENTIDENTIFICATION']._serialized_end=5169 + _globals['_AGENTREMOTECONFIG']._serialized_start=5171 + _globals['_AGENTREMOTECONFIG']._serialized_end=5256 + _globals['_AGENTCONFIGMAP']._serialized_start=5259 + _globals['_AGENTCONFIGMAP']._serialized_end=5419 + _globals['_AGENTCONFIGMAP_CONFIGMAPENTRY']._serialized_start=5341 + _globals['_AGENTCONFIGMAP_CONFIGMAPENTRY']._serialized_end=5419 + _globals['_AGENTCONFIGFILE']._serialized_start=5421 + _globals['_AGENTCONFIGFILE']._serialized_end=5474 + _globals['_CUSTOMCAPABILITIES']._serialized_start=5476 + _globals['_CUSTOMCAPABILITIES']._serialized_end=5518 + _globals['_CUSTOMMESSAGE']._serialized_start=5520 + _globals['_CUSTOMMESSAGE']._serialized_end=5583 +# @@protoc_insertion_point(module_scope) diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/opamp_pb2.pyi b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/opamp_pb2.pyi new file mode 100644 index 0000000000..1f0ad4a217 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto/opamp_pb2.pyi @@ -0,0 +1,1987 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +OpAMP: Open Agent Management Protocol (https://github.com/open-telemetry/opamp-spec)""" +import anyvalue_pb2 +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _AgentToServerFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _AgentToServerFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_AgentToServerFlags.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + AgentToServerFlags_Unspecified: _AgentToServerFlags.ValueType # 0 + AgentToServerFlags_RequestInstanceUid: _AgentToServerFlags.ValueType # 1 + """AgentToServerFlags is a bit mask. Values below define individual bits. + + The Agent requests Server go generate a new instance_uid, which will + be sent back in ServerToAgent message + """ + +class AgentToServerFlags(_AgentToServerFlags, metaclass=_AgentToServerFlagsEnumTypeWrapper): ... + +AgentToServerFlags_Unspecified: AgentToServerFlags.ValueType # 0 +AgentToServerFlags_RequestInstanceUid: AgentToServerFlags.ValueType # 1 +"""AgentToServerFlags is a bit mask. Values below define individual bits. + +The Agent requests Server go generate a new instance_uid, which will +be sent back in ServerToAgent message +""" +global___AgentToServerFlags = AgentToServerFlags + +class _ServerToAgentFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ServerToAgentFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ServerToAgentFlags.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ServerToAgentFlags_Unspecified: _ServerToAgentFlags.ValueType # 0 + ServerToAgentFlags_ReportFullState: _ServerToAgentFlags.ValueType # 1 + """Flags is a bit mask. Values below define individual bits. + + ReportFullState flag can be used by the Server if the Agent did not include the + particular bit of information in the last status report (which is an allowed + optimization) but the Server detects that it does not have it (e.g. was + restarted and lost state). The detection happens using + AgentToServer.sequence_num values. + The Server asks the Agent to report full status. + """ + ServerToAgentFlags_ReportAvailableComponents: _ServerToAgentFlags.ValueType # 2 + """ReportAvailableComponents flag can be used by the server if the Agent did + not include the full AvailableComponents message, but only the hash. + If this flag is specified, the agent will populate available_components.components + with a full description of the agent's components. + Status: [Development] + """ + +class ServerToAgentFlags(_ServerToAgentFlags, metaclass=_ServerToAgentFlagsEnumTypeWrapper): ... + +ServerToAgentFlags_Unspecified: ServerToAgentFlags.ValueType # 0 +ServerToAgentFlags_ReportFullState: ServerToAgentFlags.ValueType # 1 +"""Flags is a bit mask. Values below define individual bits. + +ReportFullState flag can be used by the Server if the Agent did not include the +particular bit of information in the last status report (which is an allowed +optimization) but the Server detects that it does not have it (e.g. was +restarted and lost state). The detection happens using +AgentToServer.sequence_num values. +The Server asks the Agent to report full status. +""" +ServerToAgentFlags_ReportAvailableComponents: ServerToAgentFlags.ValueType # 2 +"""ReportAvailableComponents flag can be used by the server if the Agent did +not include the full AvailableComponents message, but only the hash. +If this flag is specified, the agent will populate available_components.components +with a full description of the agent's components. +Status: [Development] +""" +global___ServerToAgentFlags = ServerToAgentFlags + +class _ServerCapabilities: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ServerCapabilitiesEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ServerCapabilities.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ServerCapabilities_Unspecified: _ServerCapabilities.ValueType # 0 + """The capabilities field is unspecified.""" + ServerCapabilities_AcceptsStatus: _ServerCapabilities.ValueType # 1 + """The Server can accept status reports. This bit MUST be set, since all Server + MUST be able to accept status reports. + """ + ServerCapabilities_OffersRemoteConfig: _ServerCapabilities.ValueType # 2 + """The Server can offer remote configuration to the Agent.""" + ServerCapabilities_AcceptsEffectiveConfig: _ServerCapabilities.ValueType # 4 + """The Server can accept EffectiveConfig in AgentToServer.""" + ServerCapabilities_OffersPackages: _ServerCapabilities.ValueType # 8 + """The Server can offer Packages. + Status: [Beta] + """ + ServerCapabilities_AcceptsPackagesStatus: _ServerCapabilities.ValueType # 16 + """The Server can accept Packages status. + Status: [Beta] + """ + ServerCapabilities_OffersConnectionSettings: _ServerCapabilities.ValueType # 32 + """The Server can offer connection settings. + Status: [Beta] + """ + ServerCapabilities_AcceptsConnectionSettingsRequest: _ServerCapabilities.ValueType # 64 + """The Server can accept ConnectionSettingsRequest and respond with an offer. + Status: [Development] + """ + +class ServerCapabilities(_ServerCapabilities, metaclass=_ServerCapabilitiesEnumTypeWrapper): ... + +ServerCapabilities_Unspecified: ServerCapabilities.ValueType # 0 +"""The capabilities field is unspecified.""" +ServerCapabilities_AcceptsStatus: ServerCapabilities.ValueType # 1 +"""The Server can accept status reports. This bit MUST be set, since all Server +MUST be able to accept status reports. +""" +ServerCapabilities_OffersRemoteConfig: ServerCapabilities.ValueType # 2 +"""The Server can offer remote configuration to the Agent.""" +ServerCapabilities_AcceptsEffectiveConfig: ServerCapabilities.ValueType # 4 +"""The Server can accept EffectiveConfig in AgentToServer.""" +ServerCapabilities_OffersPackages: ServerCapabilities.ValueType # 8 +"""The Server can offer Packages. +Status: [Beta] +""" +ServerCapabilities_AcceptsPackagesStatus: ServerCapabilities.ValueType # 16 +"""The Server can accept Packages status. +Status: [Beta] +""" +ServerCapabilities_OffersConnectionSettings: ServerCapabilities.ValueType # 32 +"""The Server can offer connection settings. +Status: [Beta] +""" +ServerCapabilities_AcceptsConnectionSettingsRequest: ServerCapabilities.ValueType # 64 +"""The Server can accept ConnectionSettingsRequest and respond with an offer. +Status: [Development] +""" +global___ServerCapabilities = ServerCapabilities + +class _PackageType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PackageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PackageType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PackageType_TopLevel: _PackageType.ValueType # 0 + PackageType_Addon: _PackageType.ValueType # 1 + +class PackageType(_PackageType, metaclass=_PackageTypeEnumTypeWrapper): + """The type of the package, either an addon or a top-level package. + Status: [Beta] + """ + +PackageType_TopLevel: PackageType.ValueType # 0 +PackageType_Addon: PackageType.ValueType # 1 +global___PackageType = PackageType + +class _ServerErrorResponseType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ServerErrorResponseTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ServerErrorResponseType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ServerErrorResponseType_Unknown: _ServerErrorResponseType.ValueType # 0 + """Unknown error. Something went wrong, but it is not known what exactly. + The Agent SHOULD NOT retry the message. + The error_message field may contain a description of the problem. + """ + ServerErrorResponseType_BadRequest: _ServerErrorResponseType.ValueType # 1 + """The AgentToServer message was malformed. The Agent SHOULD NOT retry + the message. + """ + ServerErrorResponseType_Unavailable: _ServerErrorResponseType.ValueType # 2 + """The Server is overloaded and unable to process the request. The Agent + should retry the message later. retry_info field may be optionally + set with additional information about retrying. + """ + +class ServerErrorResponseType(_ServerErrorResponseType, metaclass=_ServerErrorResponseTypeEnumTypeWrapper): ... + +ServerErrorResponseType_Unknown: ServerErrorResponseType.ValueType # 0 +"""Unknown error. Something went wrong, but it is not known what exactly. +The Agent SHOULD NOT retry the message. +The error_message field may contain a description of the problem. +""" +ServerErrorResponseType_BadRequest: ServerErrorResponseType.ValueType # 1 +"""The AgentToServer message was malformed. The Agent SHOULD NOT retry +the message. +""" +ServerErrorResponseType_Unavailable: ServerErrorResponseType.ValueType # 2 +"""The Server is overloaded and unable to process the request. The Agent +should retry the message later. retry_info field may be optionally +set with additional information about retrying. +""" +global___ServerErrorResponseType = ServerErrorResponseType + +class _CommandType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _CommandTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CommandType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CommandType_Restart: _CommandType.ValueType # 0 + """The Agent should restart. This request will be ignored if the Agent does not + support restart. + """ + +class CommandType(_CommandType, metaclass=_CommandTypeEnumTypeWrapper): + """Status: [Beta]""" + +CommandType_Restart: CommandType.ValueType # 0 +"""The Agent should restart. This request will be ignored if the Agent does not +support restart. +""" +global___CommandType = CommandType + +class _AgentCapabilities: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _AgentCapabilitiesEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_AgentCapabilities.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + AgentCapabilities_Unspecified: _AgentCapabilities.ValueType # 0 + """The capabilities field is unspecified.""" + AgentCapabilities_ReportsStatus: _AgentCapabilities.ValueType # 1 + """The Agent can report status. This bit MUST be set, since all Agents MUST + report status. + """ + AgentCapabilities_AcceptsRemoteConfig: _AgentCapabilities.ValueType # 2 + """The Agent can accept remote configuration from the Server.""" + AgentCapabilities_ReportsEffectiveConfig: _AgentCapabilities.ValueType # 4 + """The Agent will report EffectiveConfig in AgentToServer.""" + AgentCapabilities_AcceptsPackages: _AgentCapabilities.ValueType # 8 + """The Agent can accept package offers. + Status: [Beta] + """ + AgentCapabilities_ReportsPackageStatuses: _AgentCapabilities.ValueType # 16 + """The Agent can report package status. + Status: [Beta] + """ + AgentCapabilities_ReportsOwnTraces: _AgentCapabilities.ValueType # 32 + """The Agent can report own trace to the destination specified by + the Server via ConnectionSettingsOffers.own_traces field. + Status: [Beta] + """ + AgentCapabilities_ReportsOwnMetrics: _AgentCapabilities.ValueType # 64 + """The Agent can report own metrics to the destination specified by + the Server via ConnectionSettingsOffers.own_metrics field. + Status: [Beta] + """ + AgentCapabilities_ReportsOwnLogs: _AgentCapabilities.ValueType # 128 + """The Agent can report own logs to the destination specified by + the Server via ConnectionSettingsOffers.own_logs field. + Status: [Beta] + """ + AgentCapabilities_AcceptsOpAMPConnectionSettings: _AgentCapabilities.ValueType # 256 + """The can accept connections settings for OpAMP via + ConnectionSettingsOffers.opamp field. + Status: [Beta] + """ + AgentCapabilities_AcceptsOtherConnectionSettings: _AgentCapabilities.ValueType # 512 + """The can accept connections settings for other destinations via + ConnectionSettingsOffers.other_connections field. + Status: [Beta] + """ + AgentCapabilities_AcceptsRestartCommand: _AgentCapabilities.ValueType # 1024 + """The Agent can accept restart requests. + Status: [Beta] + """ + AgentCapabilities_ReportsHealth: _AgentCapabilities.ValueType # 2048 + """The Agent will report Health via AgentToServer.health field.""" + AgentCapabilities_ReportsRemoteConfig: _AgentCapabilities.ValueType # 4096 + """The Agent will report RemoteConfig status via AgentToServer.remote_config_status field.""" + AgentCapabilities_ReportsHeartbeat: _AgentCapabilities.ValueType # 8192 + """The Agent can report heartbeats. + This is specified by the ServerToAgent.OpAMPConnectionSettings.heartbeat_interval_seconds field. + If this capability is true, but the Server does not set a heartbeat_interval_seconds field, the + Agent should use its own configured interval, which by default will be 30s. The Server may not + know the configured interval and should not make assumptions about it. + Status: [Development] + """ + AgentCapabilities_ReportsAvailableComponents: _AgentCapabilities.ValueType # 16384 + """The agent will report AvailableComponents via the AgentToServer.available_components field. + Status: [Development] + Add new capabilities here, continuing with the least significant unused bit. + """ + +class AgentCapabilities(_AgentCapabilities, metaclass=_AgentCapabilitiesEnumTypeWrapper): ... + +AgentCapabilities_Unspecified: AgentCapabilities.ValueType # 0 +"""The capabilities field is unspecified.""" +AgentCapabilities_ReportsStatus: AgentCapabilities.ValueType # 1 +"""The Agent can report status. This bit MUST be set, since all Agents MUST +report status. +""" +AgentCapabilities_AcceptsRemoteConfig: AgentCapabilities.ValueType # 2 +"""The Agent can accept remote configuration from the Server.""" +AgentCapabilities_ReportsEffectiveConfig: AgentCapabilities.ValueType # 4 +"""The Agent will report EffectiveConfig in AgentToServer.""" +AgentCapabilities_AcceptsPackages: AgentCapabilities.ValueType # 8 +"""The Agent can accept package offers. +Status: [Beta] +""" +AgentCapabilities_ReportsPackageStatuses: AgentCapabilities.ValueType # 16 +"""The Agent can report package status. +Status: [Beta] +""" +AgentCapabilities_ReportsOwnTraces: AgentCapabilities.ValueType # 32 +"""The Agent can report own trace to the destination specified by +the Server via ConnectionSettingsOffers.own_traces field. +Status: [Beta] +""" +AgentCapabilities_ReportsOwnMetrics: AgentCapabilities.ValueType # 64 +"""The Agent can report own metrics to the destination specified by +the Server via ConnectionSettingsOffers.own_metrics field. +Status: [Beta] +""" +AgentCapabilities_ReportsOwnLogs: AgentCapabilities.ValueType # 128 +"""The Agent can report own logs to the destination specified by +the Server via ConnectionSettingsOffers.own_logs field. +Status: [Beta] +""" +AgentCapabilities_AcceptsOpAMPConnectionSettings: AgentCapabilities.ValueType # 256 +"""The can accept connections settings for OpAMP via +ConnectionSettingsOffers.opamp field. +Status: [Beta] +""" +AgentCapabilities_AcceptsOtherConnectionSettings: AgentCapabilities.ValueType # 512 +"""The can accept connections settings for other destinations via +ConnectionSettingsOffers.other_connections field. +Status: [Beta] +""" +AgentCapabilities_AcceptsRestartCommand: AgentCapabilities.ValueType # 1024 +"""The Agent can accept restart requests. +Status: [Beta] +""" +AgentCapabilities_ReportsHealth: AgentCapabilities.ValueType # 2048 +"""The Agent will report Health via AgentToServer.health field.""" +AgentCapabilities_ReportsRemoteConfig: AgentCapabilities.ValueType # 4096 +"""The Agent will report RemoteConfig status via AgentToServer.remote_config_status field.""" +AgentCapabilities_ReportsHeartbeat: AgentCapabilities.ValueType # 8192 +"""The Agent can report heartbeats. +This is specified by the ServerToAgent.OpAMPConnectionSettings.heartbeat_interval_seconds field. +If this capability is true, but the Server does not set a heartbeat_interval_seconds field, the +Agent should use its own configured interval, which by default will be 30s. The Server may not +know the configured interval and should not make assumptions about it. +Status: [Development] +""" +AgentCapabilities_ReportsAvailableComponents: AgentCapabilities.ValueType # 16384 +"""The agent will report AvailableComponents via the AgentToServer.available_components field. +Status: [Development] +Add new capabilities here, continuing with the least significant unused bit. +""" +global___AgentCapabilities = AgentCapabilities + +class _RemoteConfigStatuses: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _RemoteConfigStatusesEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_RemoteConfigStatuses.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + RemoteConfigStatuses_UNSET: _RemoteConfigStatuses.ValueType # 0 + """The value of status field is not set.""" + RemoteConfigStatuses_APPLIED: _RemoteConfigStatuses.ValueType # 1 + """Remote config was successfully applied by the Agent.""" + RemoteConfigStatuses_APPLYING: _RemoteConfigStatuses.ValueType # 2 + """Agent is currently applying the remote config that it received earlier.""" + RemoteConfigStatuses_FAILED: _RemoteConfigStatuses.ValueType # 3 + """Agent tried to apply the config received earlier, but it failed. + See error_message for more details. + """ + +class RemoteConfigStatuses(_RemoteConfigStatuses, metaclass=_RemoteConfigStatusesEnumTypeWrapper): ... + +RemoteConfigStatuses_UNSET: RemoteConfigStatuses.ValueType # 0 +"""The value of status field is not set.""" +RemoteConfigStatuses_APPLIED: RemoteConfigStatuses.ValueType # 1 +"""Remote config was successfully applied by the Agent.""" +RemoteConfigStatuses_APPLYING: RemoteConfigStatuses.ValueType # 2 +"""Agent is currently applying the remote config that it received earlier.""" +RemoteConfigStatuses_FAILED: RemoteConfigStatuses.ValueType # 3 +"""Agent tried to apply the config received earlier, but it failed. +See error_message for more details. +""" +global___RemoteConfigStatuses = RemoteConfigStatuses + +class _PackageStatusEnum: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PackageStatusEnumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PackageStatusEnum.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PackageStatusEnum_Installed: _PackageStatusEnum.ValueType # 0 + """Package is successfully installed by the Agent. + The error_message field MUST NOT be set. + """ + PackageStatusEnum_InstallPending: _PackageStatusEnum.ValueType # 1 + """Installation of this package has not yet started.""" + PackageStatusEnum_Installing: _PackageStatusEnum.ValueType # 2 + """Agent is currently installing the package. + server_offered_hash field MUST be set to indicate the version that the + Agent is installing. The error_message field MUST NOT be set. + """ + PackageStatusEnum_InstallFailed: _PackageStatusEnum.ValueType # 3 + """Agent tried to install the package but installation failed. + server_offered_hash field MUST be set to indicate the version that the Agent + tried to install. The error_message may also contain more details about + the failure. + """ + PackageStatusEnum_Downloading: _PackageStatusEnum.ValueType # 4 + """Agent is currently downloading the package. + server_offered_hash field MUST be set to indicate the version that the + Agent is installing. The error_message field MUST NOT be set. + Status: [Development] + """ + +class PackageStatusEnum(_PackageStatusEnum, metaclass=_PackageStatusEnumEnumTypeWrapper): + """The status of this package. + Status: [Beta] + """ + +PackageStatusEnum_Installed: PackageStatusEnum.ValueType # 0 +"""Package is successfully installed by the Agent. +The error_message field MUST NOT be set. +""" +PackageStatusEnum_InstallPending: PackageStatusEnum.ValueType # 1 +"""Installation of this package has not yet started.""" +PackageStatusEnum_Installing: PackageStatusEnum.ValueType # 2 +"""Agent is currently installing the package. +server_offered_hash field MUST be set to indicate the version that the +Agent is installing. The error_message field MUST NOT be set. +""" +PackageStatusEnum_InstallFailed: PackageStatusEnum.ValueType # 3 +"""Agent tried to install the package but installation failed. +server_offered_hash field MUST be set to indicate the version that the Agent +tried to install. The error_message may also contain more details about +the failure. +""" +PackageStatusEnum_Downloading: PackageStatusEnum.ValueType # 4 +"""Agent is currently downloading the package. +server_offered_hash field MUST be set to indicate the version that the +Agent is installing. The error_message field MUST NOT be set. +Status: [Development] +""" +global___PackageStatusEnum = PackageStatusEnum + +@typing_extensions.final +class AgentToServer(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + INSTANCE_UID_FIELD_NUMBER: builtins.int + SEQUENCE_NUM_FIELD_NUMBER: builtins.int + AGENT_DESCRIPTION_FIELD_NUMBER: builtins.int + CAPABILITIES_FIELD_NUMBER: builtins.int + HEALTH_FIELD_NUMBER: builtins.int + EFFECTIVE_CONFIG_FIELD_NUMBER: builtins.int + REMOTE_CONFIG_STATUS_FIELD_NUMBER: builtins.int + PACKAGE_STATUSES_FIELD_NUMBER: builtins.int + AGENT_DISCONNECT_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + CONNECTION_SETTINGS_REQUEST_FIELD_NUMBER: builtins.int + CUSTOM_CAPABILITIES_FIELD_NUMBER: builtins.int + CUSTOM_MESSAGE_FIELD_NUMBER: builtins.int + AVAILABLE_COMPONENTS_FIELD_NUMBER: builtins.int + instance_uid: builtins.bytes + """Globally unique identifier of the running instance of the Agent. SHOULD remain + unchanged for the lifetime of the Agent process. + MUST be 16 bytes long and SHOULD be generated using the UUID v7 spec. + """ + sequence_num: builtins.int + """The sequence number is incremented by 1 for every AgentToServer sent + by the Agent. This allows the Server to detect that it missed a message when + it notices that the sequence_num is not exactly by 1 greater than the previously + received one. + """ + @property + def agent_description(self) -> global___AgentDescription: + """Data that describes the Agent, its type, where it runs, etc. + May be omitted if nothing changed since last AgentToServer message. + """ + capabilities: builtins.int + """Bitmask of flags defined by AgentCapabilities enum. + All bits that are not defined in AgentCapabilities enum MUST be set to 0 by + the Agent. This allows extending the protocol and the AgentCapabilities enum + in the future such that old Agents automatically report that they don't + support the new capability. + This field MUST be always set. + """ + @property + def health(self) -> global___ComponentHealth: + """The current health of the Agent and sub-components. The top-level ComponentHealth represents + the health of the Agent overall. May be omitted if nothing changed since last AgentToServer + message. + Status: [Beta] + """ + @property + def effective_config(self) -> global___EffectiveConfig: + """The current effective configuration of the Agent. The effective configuration is + the one that is currently used by the Agent. The effective configuration may be + different from the remote configuration received from the Server earlier, e.g. + because the Agent uses a local configuration instead (or in addition). + + This field SHOULD be unset if the effective config is unchanged since the last + AgentToServer message. + """ + @property + def remote_config_status(self) -> global___RemoteConfigStatus: + """The status of the remote config that was previously received from the Server. + This field SHOULD be unset if the remote config status is unchanged since the + last AgentToServer message. + """ + @property + def package_statuses(self) -> global___PackageStatuses: + """The list of the Agent packages, including package statuses. This field SHOULD be + unset if this information is unchanged since the last AgentToServer message for + this Agent was sent in the stream. + Status: [Beta] + """ + @property + def agent_disconnect(self) -> global___AgentDisconnect: + """AgentDisconnect MUST be set in the last AgentToServer message sent from the + Agent to the Server. + """ + flags: builtins.int + """Bit flags as defined by AgentToServerFlags bit masks.""" + @property + def connection_settings_request(self) -> global___ConnectionSettingsRequest: + """A request to create connection settings. This field is set for flows where + the Agent initiates the creation of connection settings. + Status: [Development] + """ + @property + def custom_capabilities(self) -> global___CustomCapabilities: + """A message indicating custom capabilities supported by the Agent. + Status: [Development] + """ + @property + def custom_message(self) -> global___CustomMessage: + """A custom message sent from an Agent to the Server. + Status: [Development] + """ + @property + def available_components(self) -> global___AvailableComponents: + """A message indicating the components that are available for configuration on the agent. + Status: [Development] + """ + def __init__( + self, + *, + instance_uid: builtins.bytes = ..., + sequence_num: builtins.int = ..., + agent_description: global___AgentDescription | None = ..., + capabilities: builtins.int = ..., + health: global___ComponentHealth | None = ..., + effective_config: global___EffectiveConfig | None = ..., + remote_config_status: global___RemoteConfigStatus | None = ..., + package_statuses: global___PackageStatuses | None = ..., + agent_disconnect: global___AgentDisconnect | None = ..., + flags: builtins.int = ..., + connection_settings_request: global___ConnectionSettingsRequest | None = ..., + custom_capabilities: global___CustomCapabilities | None = ..., + custom_message: global___CustomMessage | None = ..., + available_components: global___AvailableComponents | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["agent_description", b"agent_description", "agent_disconnect", b"agent_disconnect", "available_components", b"available_components", "connection_settings_request", b"connection_settings_request", "custom_capabilities", b"custom_capabilities", "custom_message", b"custom_message", "effective_config", b"effective_config", "health", b"health", "package_statuses", b"package_statuses", "remote_config_status", b"remote_config_status"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["agent_description", b"agent_description", "agent_disconnect", b"agent_disconnect", "available_components", b"available_components", "capabilities", b"capabilities", "connection_settings_request", b"connection_settings_request", "custom_capabilities", b"custom_capabilities", "custom_message", b"custom_message", "effective_config", b"effective_config", "flags", b"flags", "health", b"health", "instance_uid", b"instance_uid", "package_statuses", b"package_statuses", "remote_config_status", b"remote_config_status", "sequence_num", b"sequence_num"]) -> None: ... + +global___AgentToServer = AgentToServer + +@typing_extensions.final +class AgentDisconnect(google.protobuf.message.Message): + """AgentDisconnect is the last message sent from the Agent to the Server. The Server + SHOULD forget the association of the Agent instance with the message stream. + + If the message stream is closed in the transport layer then the Server SHOULD + forget association of all Agent instances that were previously established for + this message stream using AgentConnect message, even if the corresponding + AgentDisconnect message were not explicitly received from the Agent. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___AgentDisconnect = AgentDisconnect + +@typing_extensions.final +class ConnectionSettingsRequest(google.protobuf.message.Message): + """ConnectionSettingsRequest is a request from the Agent to the Server to create + and respond with an offer of connection settings for the Agent. + Status: [Development] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + OPAMP_FIELD_NUMBER: builtins.int + @property + def opamp(self) -> global___OpAMPConnectionSettingsRequest: + """Request for OpAMP connection settings. If this field is unset + then the ConnectionSettingsRequest message is empty and is not actionable + for the Server. + """ + def __init__( + self, + *, + opamp: global___OpAMPConnectionSettingsRequest | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["opamp", b"opamp"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["opamp", b"opamp"]) -> None: ... + +global___ConnectionSettingsRequest = ConnectionSettingsRequest + +@typing_extensions.final +class OpAMPConnectionSettingsRequest(google.protobuf.message.Message): + """OpAMPConnectionSettingsRequest is a request for the Server to produce + a OpAMPConnectionSettings in its response. + Status: [Development] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CERTIFICATE_REQUEST_FIELD_NUMBER: builtins.int + @property + def certificate_request(self) -> global___CertificateRequest: + """A request to create a client certificate. This is used to initiate a + Client Signing Request (CSR) flow. + Required. + """ + def __init__( + self, + *, + certificate_request: global___CertificateRequest | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["certificate_request", b"certificate_request"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["certificate_request", b"certificate_request"]) -> None: ... + +global___OpAMPConnectionSettingsRequest = OpAMPConnectionSettingsRequest + +@typing_extensions.final +class CertificateRequest(google.protobuf.message.Message): + """Status: [Development]""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CSR_FIELD_NUMBER: builtins.int + csr: builtins.bytes + """PEM-encoded Client Certificate Signing Request (CSR), signed by client's private key. + The Server SHOULD validate the request and SHOULD respond with a + OpAMPConnectionSettings where the certificate.cert contains the issued + certificate. + """ + def __init__( + self, + *, + csr: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["csr", b"csr"]) -> None: ... + +global___CertificateRequest = CertificateRequest + +@typing_extensions.final +class AvailableComponents(google.protobuf.message.Message): + """AvailableComponents contains metadata relating to the components included + within the agent. + status: [Development] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class ComponentsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___ComponentDetails: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___ComponentDetails | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + COMPONENTS_FIELD_NUMBER: builtins.int + HASH_FIELD_NUMBER: builtins.int + @property + def components(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___ComponentDetails]: + """A map of a unique component ID to details about the component. + This may be omitted from the message if the server has not + explicitly requested it be sent by setting the ReportAvailableComponents + flag in the previous ServerToAgent message. + """ + hash: builtins.bytes + """Agent-calculated hash of the components. + This hash should be included in every AvailableComponents message. + """ + def __init__( + self, + *, + components: collections.abc.Mapping[builtins.str, global___ComponentDetails] | None = ..., + hash: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["components", b"components", "hash", b"hash"]) -> None: ... + +global___AvailableComponents = AvailableComponents + +@typing_extensions.final +class ComponentDetails(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class SubComponentMapEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___ComponentDetails: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___ComponentDetails | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + METADATA_FIELD_NUMBER: builtins.int + SUB_COMPONENT_MAP_FIELD_NUMBER: builtins.int + @property + def metadata(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[anyvalue_pb2.KeyValue]: + """Extra key/value pairs that may be used to describe the component. + The key/value pairs are according to semantic conventions, see: + https://opentelemetry.io/docs/specs/semconv/ + + For example, you may use the "code" semantic conventions to + report the location of the code for a specific component: + https://opentelemetry.io/docs/specs/semconv/attributes-registry/code/ + + Or you may use the "vcs" semantic conventions to report the + repository the component may be a part of: + https://opentelemetry.io/docs/specs/semconv/attributes-registry/vcs/ + """ + @property + def sub_component_map(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___ComponentDetails]: + """A map of component ID to sub components details. It can nest as deeply as needed to + describe the underlying system. + """ + def __init__( + self, + *, + metadata: collections.abc.Iterable[anyvalue_pb2.KeyValue] | None = ..., + sub_component_map: collections.abc.Mapping[builtins.str, global___ComponentDetails] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["metadata", b"metadata", "sub_component_map", b"sub_component_map"]) -> None: ... + +global___ComponentDetails = ComponentDetails + +@typing_extensions.final +class ServerToAgent(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + INSTANCE_UID_FIELD_NUMBER: builtins.int + ERROR_RESPONSE_FIELD_NUMBER: builtins.int + REMOTE_CONFIG_FIELD_NUMBER: builtins.int + CONNECTION_SETTINGS_FIELD_NUMBER: builtins.int + PACKAGES_AVAILABLE_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + CAPABILITIES_FIELD_NUMBER: builtins.int + AGENT_IDENTIFICATION_FIELD_NUMBER: builtins.int + COMMAND_FIELD_NUMBER: builtins.int + CUSTOM_CAPABILITIES_FIELD_NUMBER: builtins.int + CUSTOM_MESSAGE_FIELD_NUMBER: builtins.int + instance_uid: builtins.bytes + """Agent instance uid. MUST match the instance_uid field in AgentToServer message. + Used for multiplexing messages from/to multiple agents using one message stream. + """ + @property + def error_response(self) -> global___ServerErrorResponse: + """error_response is set if the Server wants to indicate that something went wrong + during processing of an AgentToServer message. If error_response is set then + all other fields below must be unset and vice versa, if any of the fields below is + set then error_response must be unset. + """ + @property + def remote_config(self) -> global___AgentRemoteConfig: + """remote_config field is set when the Server has a remote config offer for the Agent.""" + @property + def connection_settings(self) -> global___ConnectionSettingsOffers: + """This field is set when the Server wants the Agent to change one or more + of its client connection settings (destination, headers, certificate, etc). + Status: [Beta] + """ + @property + def packages_available(self) -> global___PackagesAvailable: + """This field is set when the Server has packages to offer to the Agent. + Status: [Beta] + """ + flags: builtins.int + """Bit flags as defined by ServerToAgentFlags bit masks.""" + capabilities: builtins.int + """Bitmask of flags defined by ServerCapabilities enum. + All bits that are not defined in ServerCapabilities enum MUST be set to 0 + by the Server. This allows extending the protocol and the ServerCapabilities + enum in the future such that old Servers automatically report that they + don't support the new capability. + This field MUST be set in the first ServerToAgent sent by the Server and MAY + be omitted in subsequent ServerToAgent messages by setting it to + UnspecifiedServerCapability value. + """ + @property + def agent_identification(self) -> global___AgentIdentification: + """Properties related to identification of the Agent, which can be overridden + by the Server if needed. + """ + @property + def command(self) -> global___ServerToAgentCommand: + """Allows the Server to instruct the Agent to perform a command, e.g. RESTART. This field should not be specified + with fields other than instance_uid and capabilities. If specified, other fields will be ignored and the command + will be performed. + Status: [Beta] + """ + @property + def custom_capabilities(self) -> global___CustomCapabilities: + """A message indicating custom capabilities supported by the Server. + Status: [Development] + """ + @property + def custom_message(self) -> global___CustomMessage: + """A custom message sent from the Server to an Agent. + Status: [Development] + """ + def __init__( + self, + *, + instance_uid: builtins.bytes = ..., + error_response: global___ServerErrorResponse | None = ..., + remote_config: global___AgentRemoteConfig | None = ..., + connection_settings: global___ConnectionSettingsOffers | None = ..., + packages_available: global___PackagesAvailable | None = ..., + flags: builtins.int = ..., + capabilities: builtins.int = ..., + agent_identification: global___AgentIdentification | None = ..., + command: global___ServerToAgentCommand | None = ..., + custom_capabilities: global___CustomCapabilities | None = ..., + custom_message: global___CustomMessage | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["agent_identification", b"agent_identification", "command", b"command", "connection_settings", b"connection_settings", "custom_capabilities", b"custom_capabilities", "custom_message", b"custom_message", "error_response", b"error_response", "packages_available", b"packages_available", "remote_config", b"remote_config"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["agent_identification", b"agent_identification", "capabilities", b"capabilities", "command", b"command", "connection_settings", b"connection_settings", "custom_capabilities", b"custom_capabilities", "custom_message", b"custom_message", "error_response", b"error_response", "flags", b"flags", "instance_uid", b"instance_uid", "packages_available", b"packages_available", "remote_config", b"remote_config"]) -> None: ... + +global___ServerToAgent = ServerToAgent + +@typing_extensions.final +class OpAMPConnectionSettings(google.protobuf.message.Message): + """The OpAMPConnectionSettings message is a collection of fields which comprise an + offer from the Server to the Agent to use the specified settings for OpAMP + connection. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DESTINATION_ENDPOINT_FIELD_NUMBER: builtins.int + HEADERS_FIELD_NUMBER: builtins.int + CERTIFICATE_FIELD_NUMBER: builtins.int + HEARTBEAT_INTERVAL_SECONDS_FIELD_NUMBER: builtins.int + destination_endpoint: builtins.str + """OpAMP Server URL This MUST be a WebSocket or HTTP URL and MUST be non-empty, for + example: "wss://example.com:4318/v1/opamp" + """ + @property + def headers(self) -> global___Headers: + """Optional headers to use when connecting. Typically used to set access tokens or + other authorization headers. For HTTP-based protocols the Agent should + set these in the request headers. + For example: + key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l". + """ + @property + def certificate(self) -> global___TLSCertificate: + """The Agent should use the offered certificate to connect to the destination + from now on. If the Agent is able to validate and connect using the offered + certificate the Agent SHOULD forget any previous client certificates + for this connection. + This field is optional: if omitted the client SHOULD NOT use a client-side certificate. + This field can be used to perform a client certificate revocation/rotation. + """ + heartbeat_interval_seconds: builtins.int + """The Agent MUST periodically send an AgentToServer message if the + AgentCapabilities_ReportsHeartbeat capability is true. At a minimum the instance_uid + field MUST be set. + + An HTTP Client MUST use the value as polling interval, if heartbeat_interval_seconds is non-zero. + + A heartbeat is used to keep the connection active and inform the server that the Agent + is still alive and active. + + If this field has no value or is set to 0, the Agent should not send any heartbeats. + Status: [Development] + """ + def __init__( + self, + *, + destination_endpoint: builtins.str = ..., + headers: global___Headers | None = ..., + certificate: global___TLSCertificate | None = ..., + heartbeat_interval_seconds: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "headers", b"headers"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "destination_endpoint", b"destination_endpoint", "headers", b"headers", "heartbeat_interval_seconds", b"heartbeat_interval_seconds"]) -> None: ... + +global___OpAMPConnectionSettings = OpAMPConnectionSettings + +@typing_extensions.final +class TelemetryConnectionSettings(google.protobuf.message.Message): + """The TelemetryConnectionSettings message is a collection of fields which comprise an + offer from the Server to the Agent to use the specified settings for a network + connection to report own telemetry. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DESTINATION_ENDPOINT_FIELD_NUMBER: builtins.int + HEADERS_FIELD_NUMBER: builtins.int + CERTIFICATE_FIELD_NUMBER: builtins.int + destination_endpoint: builtins.str + """The value MUST be a full URL an OTLP/HTTP/Protobuf receiver with path. Schema + SHOULD begin with "https://", for example "https://example.com:4318/v1/metrics" + The Agent MAY refuse to send the telemetry if the URL begins with "http://". + """ + @property + def headers(self) -> global___Headers: + """Optional headers to use when connecting. Typically used to set access tokens or + other authorization headers. For HTTP-based protocols the Agent should + set these in the request headers. + For example: + key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l". + """ + @property + def certificate(self) -> global___TLSCertificate: + """The Agent should use the offered certificate to connect to the destination + from now on. If the Agent is able to validate and connect using the offered + certificate the Agent SHOULD forget any previous client certificates + for this connection. + This field is optional: if omitted the client SHOULD NOT use a client-side certificate. + This field can be used to perform a client certificate revocation/rotation. + """ + def __init__( + self, + *, + destination_endpoint: builtins.str = ..., + headers: global___Headers | None = ..., + certificate: global___TLSCertificate | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "headers", b"headers"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "destination_endpoint", b"destination_endpoint", "headers", b"headers"]) -> None: ... + +global___TelemetryConnectionSettings = TelemetryConnectionSettings + +@typing_extensions.final +class OtherConnectionSettings(google.protobuf.message.Message): + """The OtherConnectionSettings message is a collection of fields which comprise an + offer from the Server to the Agent to use the specified settings for a network + connection. It is not required that all fields in this message are specified. + The Server may specify only some of the fields, in which case it means that + the Server offers the Agent to change only those fields, while keeping the + rest of the fields unchanged. + + For example the Server may send a ConnectionSettings message with only the + certificate field set, while all other fields are unset. This means that + the Server wants the Agent to use a new certificate and continue sending to + the destination it is currently sending using the current header and other + settings. + + For fields which reference other messages the field is considered unset + when the reference is unset. + + For primitive field (string) we rely on the "flags" to describe that the + field is not set (this is done to overcome the limitation of old protoc + compilers don't generate methods that allow to check for the presence of + the field. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class OtherSettingsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str = ..., + value: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + DESTINATION_ENDPOINT_FIELD_NUMBER: builtins.int + HEADERS_FIELD_NUMBER: builtins.int + CERTIFICATE_FIELD_NUMBER: builtins.int + OTHER_SETTINGS_FIELD_NUMBER: builtins.int + destination_endpoint: builtins.str + """A URL, host:port or some other destination specifier.""" + @property + def headers(self) -> global___Headers: + """Optional headers to use when connecting. Typically used to set access tokens or + other authorization headers. For HTTP-based protocols the Agent should + set these in the request headers. + For example: + key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l". + """ + @property + def certificate(self) -> global___TLSCertificate: + """The Agent should use the offered certificate to connect to the destination + from now on. If the Agent is able to validate and connect using the offered + certificate the Agent SHOULD forget any previous client certificates + for this connection. + This field is optional: if omitted the client SHOULD NOT use a client-side certificate. + This field can be used to perform a client certificate revocation/rotation. + """ + @property + def other_settings(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Other connection settings. These are Agent-specific and are up to the Agent + interpret. + """ + def __init__( + self, + *, + destination_endpoint: builtins.str = ..., + headers: global___Headers | None = ..., + certificate: global___TLSCertificate | None = ..., + other_settings: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "headers", b"headers"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "destination_endpoint", b"destination_endpoint", "headers", b"headers", "other_settings", b"other_settings"]) -> None: ... + +global___OtherConnectionSettings = OtherConnectionSettings + +@typing_extensions.final +class Headers(google.protobuf.message.Message): + """Status: [Beta]""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HEADERS_FIELD_NUMBER: builtins.int + @property + def headers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Header]: ... + def __init__( + self, + *, + headers: collections.abc.Iterable[global___Header] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["headers", b"headers"]) -> None: ... + +global___Headers = Headers + +@typing_extensions.final +class Header(google.protobuf.message.Message): + """Status: [Beta]""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str = ..., + value: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + +global___Header = Header + +@typing_extensions.final +class TLSCertificate(google.protobuf.message.Message): + """Status: [Beta] + The (cert,private_key) pair should be issued and signed by a Certificate + Authority (CA) that the destination Server recognizes. + + It is highly recommended that the private key of the CA certificate is NOT + stored on the destination Server otherwise compromising the Server will allow + a malicious actor to issue valid Server certificates which will be automatically + trusted by all agents and will allow the actor to trivially MITM Agent-to-Server + traffic of all servers that use this CA certificate for their Server-side + certificates. + + Alternatively the certificate may be self-signed, assuming the Server can + verify the certificate. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CERT_FIELD_NUMBER: builtins.int + PRIVATE_KEY_FIELD_NUMBER: builtins.int + CA_CERT_FIELD_NUMBER: builtins.int + cert: builtins.bytes + """PEM-encoded certificate. Required.""" + private_key: builtins.bytes + """PEM-encoded private key of the certificate. Required.""" + ca_cert: builtins.bytes + """PEM-encoded certificate of the signing CA. + Optional. MUST be specified if the certificate is CA-signed. + Can be stored by TLS-terminating intermediary proxies in order to verify + the connecting client's certificate in the future. + It is not recommended that the Agent accepts this CA as an authority for + any purposes. + """ + def __init__( + self, + *, + cert: builtins.bytes = ..., + private_key: builtins.bytes = ..., + ca_cert: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ca_cert", b"ca_cert", "cert", b"cert", "private_key", b"private_key"]) -> None: ... + +global___TLSCertificate = TLSCertificate + +@typing_extensions.final +class ConnectionSettingsOffers(google.protobuf.message.Message): + """Status: [Beta]""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class OtherConnectionsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___OtherConnectionSettings: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___OtherConnectionSettings | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + HASH_FIELD_NUMBER: builtins.int + OPAMP_FIELD_NUMBER: builtins.int + OWN_METRICS_FIELD_NUMBER: builtins.int + OWN_TRACES_FIELD_NUMBER: builtins.int + OWN_LOGS_FIELD_NUMBER: builtins.int + OTHER_CONNECTIONS_FIELD_NUMBER: builtins.int + hash: builtins.bytes + """Hash of all settings, including settings that may be omitted from this message + because they are unchanged. + """ + @property + def opamp(self) -> global___OpAMPConnectionSettings: + """Settings to connect to the OpAMP Server. + If this field is not set then the Agent should assume that the settings are + unchanged and should continue using existing settings. + The Agent MUST verify the offered connection settings by actually connecting + before accepting the setting to ensure it does not loose access to the OpAMP + Server due to invalid settings. + """ + @property + def own_metrics(self) -> global___TelemetryConnectionSettings: + """Settings to connect to an OTLP metrics backend to send Agent's own metrics to. + If this field is not set then the Agent should assume that the settings + are unchanged. + + Once accepted the Agent should periodically send to the specified destination + its own metrics, i.e. metrics of the Agent process and any custom metrics that + describe the Agent state. + + All attributes specified in the identifying_attributes field in AgentDescription + message SHOULD be also specified in the Resource of the reported OTLP metrics. + + Attributes specified in the non_identifying_attributes field in + AgentDescription message may be also specified in the Resource of the reported + OTLP metrics, in which case they SHOULD have exactly the same values. + + Process metrics MUST follow the conventions for processes: + https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/process-metrics.md + """ + @property + def own_traces(self) -> global___TelemetryConnectionSettings: + """Similar to own_metrics, but for traces.""" + @property + def own_logs(self) -> global___TelemetryConnectionSettings: + """Similar to own_metrics, but for logs.""" + @property + def other_connections(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___OtherConnectionSettings]: + """Another set of connection settings, with a string name associated with each. + How the Agent uses these is Agent-specific. Typically the name represents + the name of the destination to connect to (as it is known to the Agent). + If this field is not set then the Agent should assume that the other_connections + settings are unchanged. + """ + def __init__( + self, + *, + hash: builtins.bytes = ..., + opamp: global___OpAMPConnectionSettings | None = ..., + own_metrics: global___TelemetryConnectionSettings | None = ..., + own_traces: global___TelemetryConnectionSettings | None = ..., + own_logs: global___TelemetryConnectionSettings | None = ..., + other_connections: collections.abc.Mapping[builtins.str, global___OtherConnectionSettings] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["opamp", b"opamp", "own_logs", b"own_logs", "own_metrics", b"own_metrics", "own_traces", b"own_traces"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["hash", b"hash", "opamp", b"opamp", "other_connections", b"other_connections", "own_logs", b"own_logs", "own_metrics", b"own_metrics", "own_traces", b"own_traces"]) -> None: ... + +global___ConnectionSettingsOffers = ConnectionSettingsOffers + +@typing_extensions.final +class PackagesAvailable(google.protobuf.message.Message): + """List of packages that the Server offers to the Agent. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class PackagesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___PackageAvailable: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___PackageAvailable | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + PACKAGES_FIELD_NUMBER: builtins.int + ALL_PACKAGES_HASH_FIELD_NUMBER: builtins.int + @property + def packages(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___PackageAvailable]: + """Map of packages. Keys are package names, values are the packages available for download.""" + all_packages_hash: builtins.bytes + """Aggregate hash of all remotely installed packages. The Agent SHOULD include this + value in subsequent PackageStatuses messages. This in turn allows the management + Server to identify that a different set of packages is available for the Agent + and specify the available packages in the next ServerToAgent message. + + This field MUST be always set if the management Server supports packages + of agents. + + The hash is calculated as an aggregate of all packages names and content. + """ + def __init__( + self, + *, + packages: collections.abc.Mapping[builtins.str, global___PackageAvailable] | None = ..., + all_packages_hash: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["all_packages_hash", b"all_packages_hash", "packages", b"packages"]) -> None: ... + +global___PackagesAvailable = PackagesAvailable + +@typing_extensions.final +class PackageAvailable(google.protobuf.message.Message): + """Each Agent is composed of one or more packages. A package has a name and + content stored in a file. The content of the files, functionality + provided by the packages, how they are stored and used by the Agent side is Agent + type-specific and is outside the concerns of the OpAMP protocol. + + If the Agent does not have an installed package with the specified name then + it SHOULD download it from the specified URL and install it. + + If the Agent already has an installed package with the specified name + but with a different hash then the Agent SHOULD download and + install the package again, since it is a different version of the same package. + + If the Agent has an installed package with the specified name and the same + hash then the Agent does not need to do anything, it already + has the right version of the package. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TYPE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + FILE_FIELD_NUMBER: builtins.int + HASH_FIELD_NUMBER: builtins.int + type: global___PackageType.ValueType + version: builtins.str + """The package version that is available on the Server side. The Agent may for + example use this information to avoid downloading a package that was previously + already downloaded and failed to install. + """ + @property + def file(self) -> global___DownloadableFile: + """The downloadable file of the package.""" + hash: builtins.bytes + """The hash of the package. SHOULD be calculated based on all other fields of the + PackageAvailable message and content of the file of the package. The hash is + used by the Agent to determine if the package it has is different from the + package the Server is offering. + """ + def __init__( + self, + *, + type: global___PackageType.ValueType = ..., + version: builtins.str = ..., + file: global___DownloadableFile | None = ..., + hash: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["file", b"file"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["file", b"file", "hash", b"hash", "type", b"type", "version", b"version"]) -> None: ... + +global___PackageAvailable = PackageAvailable + +@typing_extensions.final +class DownloadableFile(google.protobuf.message.Message): + """Status: [Beta]""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DOWNLOAD_URL_FIELD_NUMBER: builtins.int + CONTENT_HASH_FIELD_NUMBER: builtins.int + SIGNATURE_FIELD_NUMBER: builtins.int + HEADERS_FIELD_NUMBER: builtins.int + download_url: builtins.str + """The URL from which the file can be downloaded using HTTP GET request. + The Server at the specified URL SHOULD support range requests + to allow for resuming downloads. + """ + content_hash: builtins.bytes + """The hash of the file content. Can be used by the Agent to verify that the file + was downloaded correctly. + """ + signature: builtins.bytes + """Optional signature of the file content. Can be used by the Agent to verify the + authenticity of the downloaded file, for example can be the + [detached GPG signature](https://www.gnupg.org/gph/en/manual/x135.html#AEN160). + The exact signing and verification method is Agent specific. See + https://github.com/open-telemetry/opamp-spec/blob/main/specification.md#code-signing + for recommendations. + """ + @property + def headers(self) -> global___Headers: + """Optional headers to use when downloading a file. Typically used to set + access tokens or other authorization headers. For HTTP-based protocols + the Agent should set these in the request headers. + For example: + key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l". + Status: [Development] + """ + def __init__( + self, + *, + download_url: builtins.str = ..., + content_hash: builtins.bytes = ..., + signature: builtins.bytes = ..., + headers: global___Headers | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["headers", b"headers"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["content_hash", b"content_hash", "download_url", b"download_url", "headers", b"headers", "signature", b"signature"]) -> None: ... + +global___DownloadableFile = DownloadableFile + +@typing_extensions.final +class ServerErrorResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TYPE_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + RETRY_INFO_FIELD_NUMBER: builtins.int + type: global___ServerErrorResponseType.ValueType + error_message: builtins.str + """Error message in the string form, typically human readable.""" + @property + def retry_info(self) -> global___RetryInfo: + """Additional information about retrying if type==UNAVAILABLE.""" + def __init__( + self, + *, + type: global___ServerErrorResponseType.ValueType = ..., + error_message: builtins.str = ..., + retry_info: global___RetryInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["Details", b"Details", "retry_info", b"retry_info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["Details", b"Details", "error_message", b"error_message", "retry_info", b"retry_info", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["Details", b"Details"]) -> typing_extensions.Literal["retry_info"] | None: ... + +global___ServerErrorResponse = ServerErrorResponse + +@typing_extensions.final +class RetryInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RETRY_AFTER_NANOSECONDS_FIELD_NUMBER: builtins.int + retry_after_nanoseconds: builtins.int + def __init__( + self, + *, + retry_after_nanoseconds: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["retry_after_nanoseconds", b"retry_after_nanoseconds"]) -> None: ... + +global___RetryInfo = RetryInfo + +@typing_extensions.final +class ServerToAgentCommand(google.protobuf.message.Message): + """ServerToAgentCommand is sent from the Server to the Agent to request that the Agent + perform a command. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TYPE_FIELD_NUMBER: builtins.int + type: global___CommandType.ValueType + def __init__( + self, + *, + type: global___CommandType.ValueType = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["type", b"type"]) -> None: ... + +global___ServerToAgentCommand = ServerToAgentCommand + +@typing_extensions.final +class AgentDescription(google.protobuf.message.Message): + """////////////////////////////////////////////////////////////////////////////////// + Status reporting + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IDENTIFYING_ATTRIBUTES_FIELD_NUMBER: builtins.int + NON_IDENTIFYING_ATTRIBUTES_FIELD_NUMBER: builtins.int + @property + def identifying_attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[anyvalue_pb2.KeyValue]: + """Attributes that identify the Agent. + Keys/values are according to OpenTelemetry semantic conventions, see: + https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions + + For standalone running Agents (such as OpenTelemetry Collector) the following + attributes SHOULD be specified: + - service.name should be set to a reverse FQDN that uniquely identifies the + Agent type, e.g. "io.opentelemetry.collector" + - service.namespace if it is used in the environment where the Agent runs. + - service.version should be set to version number of the Agent build. + - service.instance.id should be set. It may be set equal to the Agent's + instance uid (equal to ServerToAgent.instance_uid field) or any other value + that uniquely identifies the Agent in combination with other attributes. + - any other attributes that are necessary for uniquely identifying the Agent's + own telemetry. + + The Agent SHOULD also include these attributes in the Resource of its own + telemetry. The combination of identifying attributes SHOULD be sufficient to + uniquely identify the Agent's own telemetry in the destination system to which + the Agent sends its own telemetry. + """ + @property + def non_identifying_attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[anyvalue_pb2.KeyValue]: + """Attributes that do not necessarily identify the Agent but help describe + where it runs. + The following attributes SHOULD be included: + - os.type, os.version - to describe where the Agent runs. + - host.* to describe the host the Agent runs on. + - cloud.* to describe the cloud where the host is located. + - any other relevant Resource attributes that describe this Agent and the + environment it runs in. + - any user-defined attributes that the end user would like to associate + with this Agent. + """ + def __init__( + self, + *, + identifying_attributes: collections.abc.Iterable[anyvalue_pb2.KeyValue] | None = ..., + non_identifying_attributes: collections.abc.Iterable[anyvalue_pb2.KeyValue] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["identifying_attributes", b"identifying_attributes", "non_identifying_attributes", b"non_identifying_attributes"]) -> None: ... + +global___AgentDescription = AgentDescription + +@typing_extensions.final +class ComponentHealth(google.protobuf.message.Message): + """The health of the Agent and sub-components + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class ComponentHealthMapEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___ComponentHealth: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___ComponentHealth | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + HEALTHY_FIELD_NUMBER: builtins.int + START_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + LAST_ERROR_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + STATUS_TIME_UNIX_NANO_FIELD_NUMBER: builtins.int + COMPONENT_HEALTH_MAP_FIELD_NUMBER: builtins.int + healthy: builtins.bool + """Set to true if the component is up and healthy.""" + start_time_unix_nano: builtins.int + """Timestamp since the component is up, i.e. when the component was started. + Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + If the component is not running MUST be set to 0. + """ + last_error: builtins.str + """Human-readable error message if the component is in erroneous state. SHOULD be set + when healthy==false. + """ + status: builtins.str + """Component status represented as a string. The status values are defined by agent-specific + semantics and not at the protocol level. + """ + status_time_unix_nano: builtins.int + """The time when the component status was observed. Value is UNIX Epoch time in + nanoseconds since 00:00:00 UTC on 1 January 1970. + """ + @property + def component_health_map(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___ComponentHealth]: + """A map to store more granular, sub-component health. It can nest as deeply as needed to + describe the underlying system. + """ + def __init__( + self, + *, + healthy: builtins.bool = ..., + start_time_unix_nano: builtins.int = ..., + last_error: builtins.str = ..., + status: builtins.str = ..., + status_time_unix_nano: builtins.int = ..., + component_health_map: collections.abc.Mapping[builtins.str, global___ComponentHealth] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["component_health_map", b"component_health_map", "healthy", b"healthy", "last_error", b"last_error", "start_time_unix_nano", b"start_time_unix_nano", "status", b"status", "status_time_unix_nano", b"status_time_unix_nano"]) -> None: ... + +global___ComponentHealth = ComponentHealth + +@typing_extensions.final +class EffectiveConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CONFIG_MAP_FIELD_NUMBER: builtins.int + @property + def config_map(self) -> global___AgentConfigMap: + """The effective config of the Agent.""" + def __init__( + self, + *, + config_map: global___AgentConfigMap | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["config_map", b"config_map"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["config_map", b"config_map"]) -> None: ... + +global___EffectiveConfig = EffectiveConfig + +@typing_extensions.final +class RemoteConfigStatus(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LAST_REMOTE_CONFIG_HASH_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + last_remote_config_hash: builtins.bytes + """The hash of the remote config that was last received by this Agent in the + AgentRemoteConfig.config_hash field. + The Server SHOULD compare this hash with the config hash + it has for the Agent and if the hashes are different the Server MUST include + the remote_config field in the response in the ServerToAgent message. + """ + status: global___RemoteConfigStatuses.ValueType + error_message: builtins.str + """Optional error message if status==FAILED.""" + def __init__( + self, + *, + last_remote_config_hash: builtins.bytes = ..., + status: global___RemoteConfigStatuses.ValueType = ..., + error_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["error_message", b"error_message", "last_remote_config_hash", b"last_remote_config_hash", "status", b"status"]) -> None: ... + +global___RemoteConfigStatus = RemoteConfigStatus + +@typing_extensions.final +class PackageStatuses(google.protobuf.message.Message): + """The PackageStatuses message describes the status of all packages that the Agent + has or was offered. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class PackagesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___PackageStatus: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___PackageStatus | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + PACKAGES_FIELD_NUMBER: builtins.int + SERVER_PROVIDED_ALL_PACKAGES_HASH_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + @property + def packages(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___PackageStatus]: + """A map of PackageStatus messages, where the keys are package names. + The key MUST match the name field of PackageStatus message. + """ + server_provided_all_packages_hash: builtins.bytes + """The aggregate hash of all packages that this Agent previously received from the + Server via PackagesAvailable message. + + The Server SHOULD compare this hash to the aggregate hash of all packages that + it has for this Agent and if the hashes are different the Server SHOULD send + an PackagesAvailable message to the Agent. + """ + error_message: builtins.str + """This field is set if the Agent encountered an error when processing the + PackagesAvailable message and that error is not related to any particular single + package. + The field must be unset is there were no processing errors. + """ + def __init__( + self, + *, + packages: collections.abc.Mapping[builtins.str, global___PackageStatus] | None = ..., + server_provided_all_packages_hash: builtins.bytes = ..., + error_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["error_message", b"error_message", "packages", b"packages", "server_provided_all_packages_hash", b"server_provided_all_packages_hash"]) -> None: ... + +global___PackageStatuses = PackageStatuses + +@typing_extensions.final +class PackageStatus(google.protobuf.message.Message): + """The status of a single package. + Status: [Beta] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + AGENT_HAS_VERSION_FIELD_NUMBER: builtins.int + AGENT_HAS_HASH_FIELD_NUMBER: builtins.int + SERVER_OFFERED_VERSION_FIELD_NUMBER: builtins.int + SERVER_OFFERED_HASH_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + DOWNLOAD_DETAILS_FIELD_NUMBER: builtins.int + name: builtins.str + """Package name. MUST be always set and MUST match the key in the packages field + of PackageStatuses message. + """ + agent_has_version: builtins.str + """The version of the package that the Agent has. + MUST be set if the Agent has this package. + MUST be empty if the Agent does not have this package. This may be the case + for example if the package was offered by the Server but failed to install + and the Agent did not have this package previously. + """ + agent_has_hash: builtins.bytes + """The hash of the package that the Agent has. + MUST be set if the Agent has this package. + MUST be empty if the Agent does not have this package. This may be the case for + example if the package was offered by the Server but failed to install and the + Agent did not have this package previously. + """ + server_offered_version: builtins.str + """The version of the package that the Server offered to the Agent. + MUST be set if the installation of the package is initiated by an earlier offer + from the Server to install this package. + + MUST be empty if the Agent has this package but it was installed locally and + was not offered by the Server. + + Note that it is possible for both agent_has_version and server_offered_version + fields to be set and to have different values. This is for example possible if + the Agent already has a version of the package successfully installed, the Server + offers a different version, but the Agent fails to install that version. + """ + server_offered_hash: builtins.bytes + """The hash of the package that the Server offered to the Agent. + MUST be set if the installation of the package is initiated by an earlier + offer from the Server to install this package. + + MUST be empty if the Agent has this package but it was installed locally and + was not offered by the Server. + + Note that it is possible for both agent_has_hash and server_offered_hash + fields to be set and to have different values. This is for example possible if + the Agent already has a version of the package successfully installed, the + Server offers a different version, but the Agent fails to install that version. + """ + status: global___PackageStatusEnum.ValueType + error_message: builtins.str + """Error message if the status is erroneous.""" + @property + def download_details(self) -> global___PackageDownloadDetails: + """Optional details that may be of interest to a user. + Should only be set if status is Downloading. + Status: [Development] + """ + def __init__( + self, + *, + name: builtins.str = ..., + agent_has_version: builtins.str = ..., + agent_has_hash: builtins.bytes = ..., + server_offered_version: builtins.str = ..., + server_offered_hash: builtins.bytes = ..., + status: global___PackageStatusEnum.ValueType = ..., + error_message: builtins.str = ..., + download_details: global___PackageDownloadDetails | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["download_details", b"download_details"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["agent_has_hash", b"agent_has_hash", "agent_has_version", b"agent_has_version", "download_details", b"download_details", "error_message", b"error_message", "name", b"name", "server_offered_hash", b"server_offered_hash", "server_offered_version", b"server_offered_version", "status", b"status"]) -> None: ... + +global___PackageStatus = PackageStatus + +@typing_extensions.final +class PackageDownloadDetails(google.protobuf.message.Message): + """Additional details that an agent can use to describe an in-progress package download. + Status: [Development] + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DOWNLOAD_PERCENT_FIELD_NUMBER: builtins.int + DOWNLOAD_BYTES_PER_SECOND_FIELD_NUMBER: builtins.int + download_percent: builtins.float + """The package download progress as a percentage.""" + download_bytes_per_second: builtins.float + """The current package download rate in bytes per second.""" + def __init__( + self, + *, + download_percent: builtins.float = ..., + download_bytes_per_second: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["download_bytes_per_second", b"download_bytes_per_second", "download_percent", b"download_percent"]) -> None: ... + +global___PackageDownloadDetails = PackageDownloadDetails + +@typing_extensions.final +class AgentIdentification(google.protobuf.message.Message): + """Properties related to identification of the Agent, which can be overridden + by the Server if needed + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NEW_INSTANCE_UID_FIELD_NUMBER: builtins.int + new_instance_uid: builtins.bytes + """When new_instance_uid is set, Agent MUST update instance_uid + to the value provided and use it for all further communication. + MUST be 16 bytes long and SHOULD be generated using the UUID v7 spec. + """ + def __init__( + self, + *, + new_instance_uid: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["new_instance_uid", b"new_instance_uid"]) -> None: ... + +global___AgentIdentification = AgentIdentification + +@typing_extensions.final +class AgentRemoteConfig(google.protobuf.message.Message): + """/////////////////////////////////////////////////////////////////////////////////// + Config messages + /////////////////////////////////////////////////////////////////////////////////// + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CONFIG_FIELD_NUMBER: builtins.int + CONFIG_HASH_FIELD_NUMBER: builtins.int + @property + def config(self) -> global___AgentConfigMap: + """Agent config offered by the management Server to the Agent instance. SHOULD NOT be + set if the config for this Agent has not changed since it was last requested (i.e. + AgentConfigRequest.last_remote_config_hash field is equal to + AgentConfigResponse.config_hash field). + """ + config_hash: builtins.bytes + """Hash of "config". The Agent SHOULD include this value in subsequent + RemoteConfigStatus messages in the last_remote_config_hash field. This in turn + allows the management Server to identify that a new config is available for the Agent. + + This field MUST be always set if the management Server supports remote configuration + of agents. + + Management Server must choose a hashing function that guarantees lack of hash + collisions in practice. + """ + def __init__( + self, + *, + config: global___AgentConfigMap | None = ..., + config_hash: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["config", b"config"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["config", b"config", "config_hash", b"config_hash"]) -> None: ... + +global___AgentRemoteConfig = AgentRemoteConfig + +@typing_extensions.final +class AgentConfigMap(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class ConfigMapEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> global___AgentConfigFile: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: global___AgentConfigFile | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + CONFIG_MAP_FIELD_NUMBER: builtins.int + @property + def config_map(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___AgentConfigFile]: + """Map of configs. Keys are config file names or config section names. + The configuration is assumed to be a collection of one or more named config files + or sections. + For agents that use a single config file or section the map SHOULD contain a single + entry and the key may be an empty string. + """ + def __init__( + self, + *, + config_map: collections.abc.Mapping[builtins.str, global___AgentConfigFile] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["config_map", b"config_map"]) -> None: ... + +global___AgentConfigMap = AgentConfigMap + +@typing_extensions.final +class AgentConfigFile(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + BODY_FIELD_NUMBER: builtins.int + CONTENT_TYPE_FIELD_NUMBER: builtins.int + body: builtins.bytes + """Config file or section body. The content, format and encoding depends on the Agent + type. The content_type field may optionally describe the MIME type of the body. + """ + content_type: builtins.str + """Optional MIME Content-Type that describes what's in the body field, for + example "text/yaml". + """ + def __init__( + self, + *, + body: builtins.bytes = ..., + content_type: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["body", b"body", "content_type", b"content_type"]) -> None: ... + +global___AgentConfigFile = AgentConfigFile + +@typing_extensions.final +class CustomCapabilities(google.protobuf.message.Message): + """/////////////////////////////////////////////////////////////////////////////////// + Custom messages + /////////////////////////////////////////////////////////////////////////////////// + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CAPABILITIES_FIELD_NUMBER: builtins.int + @property + def capabilities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """A list of custom capabilities that are supported. Each capability is a reverse FQDN + with optional version information that uniquely identifies the custom capability + and should match a capability specified in a supported CustomMessage. + Status: [Development] + """ + def __init__( + self, + *, + capabilities: collections.abc.Iterable[builtins.str] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["capabilities", b"capabilities"]) -> None: ... + +global___CustomCapabilities = CustomCapabilities + +@typing_extensions.final +class CustomMessage(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CAPABILITY_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + capability: builtins.str + """A reverse FQDN that uniquely identifies the capability and matches one of the + capabilities in the CustomCapabilities message. + Status: [Development] + """ + type: builtins.str + """Type of message within the capability. The capability defines the types of custom + messages that are used to implement the capability. The type must only be unique + within the capability. + Status: [Development] + """ + data: builtins.bytes + """Binary data of the message. The capability must specify the format of the contents + of the data for each custom message type it defines. + Status: [Development] + """ + def __init__( + self, + *, + capability: builtins.str = ..., + type: builtins.str = ..., + data: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["capability", b"capability", "data", b"data", "type", b"type"]) -> None: ... + +global___CustomMessage = CustomMessage diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/base.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/base.py new file mode 100644 index 0000000000..ac4e6866bc --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/base.py @@ -0,0 +1,40 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import abc +from typing import Mapping + +from opentelemetry._opamp.proto import opamp_pb2 + +base_headers = { + "Content-Type": "application/x-protobuf", +} + + +class HttpTransport(abc.ABC): + @abc.abstractmethod + def send( + self, + *, + url: str, + headers: Mapping[str, str], + data: bytes, + timeout_millis: int, + tls_certificate: str | bool, + tls_client_certificate: str | None = None, + tls_client_key: str | None = None, + ) -> opamp_pb2.ServerToAgent: + pass diff --git a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/package.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/exceptions.py similarity index 93% rename from instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/package.py rename to opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/exceptions.py index c967086b1f..e57aeb53f7 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/package.py +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/exceptions.py @@ -13,4 +13,5 @@ # limitations under the License. -_instruments = ("boto~=2.0",) +class OpAMPException(Exception): + pass diff --git a/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/requests.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/requests.py new file mode 100644 index 0000000000..6c29a22d23 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/transport/requests.py @@ -0,0 +1,66 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import logging +from typing import Mapping + +import requests + +from opentelemetry._opamp import messages +from opentelemetry._opamp.transport.base import HttpTransport, base_headers +from opentelemetry._opamp.transport.exceptions import OpAMPException + +logger = logging.getLogger(__name__) + + +class RequestsTransport(HttpTransport): + def __init__(self, session: requests.Session | None = None): + self.session = requests.Session() if session is None else session + + def send( + self, + *, + url: str, + headers: Mapping[str, str], + data: bytes, + timeout_millis: int, + tls_certificate: str | bool, + tls_client_certificate: str | None = None, + tls_client_key: str | None = None, + ): + headers = {**base_headers, **headers} + timeout: float = timeout_millis / 1e3 + client_cert = ( + (tls_client_certificate, tls_client_key) + if tls_client_certificate and tls_client_key + else tls_client_certificate + ) + try: + response = self.session.post( + url, + headers=headers, + data=data, + timeout=timeout, + verify=tls_certificate, + cert=client_cert, + ) + response.raise_for_status() + except Exception as exc: + raise OpAMPException(str(exc)) from exc + + message = messages.decode_message(response.content) + + return message diff --git a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/version.py b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/version.py similarity index 95% rename from instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/version.py rename to opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/version.py index ed89ddd1cc..32e2a06f96 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/version.py +++ b/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.3b0.dev" diff --git a/opamp/opentelemetry-opamp-client/test-requirements.in b/opamp/opentelemetry-opamp-client/test-requirements.in new file mode 100644 index 0000000000..67c1f299b8 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/test-requirements.in @@ -0,0 +1,18 @@ +colorama>=0.4.6 +iniconfig>=2.0.0 +packaging>=24.0 +pluggy>=1.5.0 +protobuf>=5.29.5 +pytest>=7.4.4 +pytest-vcr>=1.0.2 ; python_version > '3.9' and platform_python_implementation !='PyPy' +pyyaml>=6.0.2 +vcrpy>=7.0.0 ; python_version > '3.9' and platform_python_implementation !='PyPy' +yarl>=1.20.1 +requests>=2.32.2 +urllib3>=2.5.0 +uuid-utils>=0.11.0 +wrapt>=1.16.0 +-e opamp/opentelemetry-opamp-client + +# for pylint +tomli>=1.1.0 diff --git a/opamp/opentelemetry-opamp-client/test-requirements.latest.txt b/opamp/opentelemetry-opamp-client/test-requirements.latest.txt new file mode 100644 index 0000000000..68196f9992 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/test-requirements.latest.txt @@ -0,0 +1,90 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --python 3.10 --universal -c dev-requirements.txt opamp/opentelemetry-opamp-client/test-requirements.in -o opamp/opentelemetry-opamp-client/test-requirements.latest.txt --no-emit-package opentelemetry-api +-e opamp/opentelemetry-opamp-client + # via -r opamp/opentelemetry-opamp-client/test-requirements.in +certifi==2025.7.9 + # via requests +charset-normalizer==3.4.2 + # via requests +colorama==0.4.6 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +exceptiongroup==1.3.0 ; python_full_version < '3.11' + # via pytest +idna==3.10 + # via + # requests + # yarl +importlib-metadata==8.7.0 + # via opentelemetry-api +iniconfig==2.1.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +multidict==6.6.3 + # via yarl +packaging==25.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +pluggy==1.6.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +propcache==0.3.2 + # via yarl +protobuf==6.31.1 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # opentelemetry-opamp-client +pytest==7.4.4 + # via + # -c dev-requirements.txt + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest-vcr +pytest-vcr==1.0.2 ; platform_python_implementation != 'PyPy' + # via -r opamp/opentelemetry-opamp-client/test-requirements.in +pyyaml==6.0.2 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # vcrpy +requests==2.32.3 + # via + # -c dev-requirements.txt + # -r opamp/opentelemetry-opamp-client/test-requirements.in +tomli==2.2.1 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +typing-extensions==4.14.0 + # via + # exceptiongroup + # multidict + # opentelemetry-api +urllib3==2.5.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # requests + # vcrpy +uuid-utils==0.11.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # opentelemetry-opamp-client +vcrpy==7.0.0 ; platform_python_implementation != 'PyPy' + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest-vcr +wrapt==1.17.2 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # vcrpy +yarl==1.20.1 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # vcrpy +zipp==3.23.0 + # via importlib-metadata + +# The following packages were excluded from the output: +# opentelemetry-api diff --git a/opamp/opentelemetry-opamp-client/test-requirements.lowest.txt b/opamp/opentelemetry-opamp-client/test-requirements.lowest.txt new file mode 100644 index 0000000000..44acbe65ec --- /dev/null +++ b/opamp/opentelemetry-opamp-client/test-requirements.lowest.txt @@ -0,0 +1,86 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --python 3.10 --universal --resolution lowest -c dev-requirements.txt opamp/opentelemetry-opamp-client/test-requirements.in -o opamp/opentelemetry-opamp-client/test-requirements.lowest.txt --no-emit-package opentelemetry-api +-e opamp/opentelemetry-opamp-client + # via -r opamp/opentelemetry-opamp-client/test-requirements.in +certifi==2017.4.17 + # via requests +charset-normalizer==2.0.0 + # via requests +colorama==0.4.6 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +deprecated==1.2.6 + # via opentelemetry-api +exceptiongroup==1.0.0 ; python_full_version < '3.11' + # via pytest +idna==2.5 + # via + # requests + # yarl +iniconfig==2.0.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +multidict==4.0.0 + # via yarl +packaging==24.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +pluggy==1.6.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +propcache==0.2.1 + # via yarl +protobuf==5.29.5 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # opentelemetry-opamp-client +pytest==7.4.4 + # via + # -c dev-requirements.txt + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest-vcr +pytest-vcr==1.0.2 ; platform_python_implementation != 'PyPy' + # via -r opamp/opentelemetry-opamp-client/test-requirements.in +pyyaml==6.0.2 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # vcrpy +requests==2.32.3 + # via + # -c dev-requirements.txt + # -r opamp/opentelemetry-opamp-client/test-requirements.in +setuptools==16.0 + # via opentelemetry-api +tomli==1.1.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest +urllib3==2.5.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # requests + # vcrpy +uuid-utils==0.11.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # opentelemetry-opamp-client +vcrpy==7.0.0 ; platform_python_implementation != 'PyPy' + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # pytest-vcr +wrapt==1.16.0 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # deprecated + # vcrpy +yarl==1.20.1 + # via + # -r opamp/opentelemetry-opamp-client/test-requirements.in + # vcrpy + +# The following packages were excluded from the output: +# opentelemetry-api diff --git a/opamp/opentelemetry-opamp-client/tests/opamp/README.md b/opamp/opentelemetry-opamp-client/tests/opamp/README.md new file mode 100644 index 0000000000..b36067236e --- /dev/null +++ b/opamp/opentelemetry-opamp-client/tests/opamp/README.md @@ -0,0 +1,23 @@ +## How to record tests cassettes + +### Run the server + +Checkout [opentelemetry-go](https://github.com/open-telemetry/opentelemetry-go) and build it with: + +``` +make +``` + +Then run the example server with: + +``` +./internal/examples/server/bin/server +``` + +### Record the cassettes + +We are using `vcr.py` for recording HTTP responses from the server, it works by serializing the response in a yaml file and then replying +it. `vcr.py` will record a new cassettes only if it does not find it so to redo a recording you need to delete the recording in the +`cassettes/` directory. + +Because of vcr.py issues with older urllib3 versions tests are skipped with Python older than 3.10. diff --git a/opamp/opentelemetry-opamp-client/tests/opamp/cassettes/test_connection_remote_config_status_heartbeat_disconnection.yaml b/opamp/opentelemetry-opamp-client/tests/opamp/cassettes/test_connection_remote_config_status_heartbeat_disconnection.yaml new file mode 100644 index 0000000000..df254d3714 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/tests/opamp/cassettes/test_connection_remote_config_status_heartbeat_disconnection.yaml @@ -0,0 +1,136 @@ +interactions: +- request: + body: !!binary | + ChABnL5b5k5046/FazbAJ7r4Gj0KFQoMc2VydmljZS5uYW1lEgUKA2ZvbwokChtkZXBsb3ltZW50 + LmVudmlyb25tZW50Lm5hbWUSBQoDZm9vIIdg + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '84' + Content-Type: + - application/x-protobuf + User-Agent: + - OTel-OpAMP-Python/0.1b0.dev + method: POST + uri: https://localhost:4320/v1/opamp + response: + body: + string: !!binary | + ChABnL5b5k5046/FazbAJ7r4GioKBgoECgASABIg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZ + G3hSuFUwAVIA + headers: + Content-Length: + - '66' + Content-Type: + - application/x-protobuf + Date: + - Thu, 05 Mar 2026 14:16:59 GMT + status: + code: 200 + message: OK +- request: + body: !!binary | + ChABnL5b5k5046/FazbAJ7r4EAEgh2A6JAog47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hS + uFUQAQ== + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '61' + Content-Type: + - application/x-protobuf + User-Agent: + - OTel-OpAMP-Python/0.1b0.dev + method: POST + uri: https://localhost:4320/v1/opamp + response: + body: + string: !!binary | + ChABnL5b5k5046/FazbAJ7r4GioKBgoECgASABIg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZ + G3hSuFVSAA== + headers: + Content-Length: + - '64' + Content-Type: + - application/x-protobuf + Date: + - Thu, 05 Mar 2026 14:16:59 GMT + status: + code: 200 + message: OK +- request: + body: !!binary | + ChABnL5b5k5046/FazbAJ7r4EAIgh2A= + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '23' + Content-Type: + - application/x-protobuf + User-Agent: + - OTel-OpAMP-Python/0.1b0.dev + method: POST + uri: https://localhost:4320/v1/opamp + response: + body: + string: !!binary | + ChABnL5b5k5046/FazbAJ7r4GioKBgoECgASABIg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZ + G3hSuFVSAA== + headers: + Content-Length: + - '64' + Content-Type: + - application/x-protobuf + Date: + - Thu, 05 Mar 2026 14:17:00 GMT + status: + code: 200 + message: OK +- request: + body: !!binary | + ChABnL5b5k5046/FazbAJ7r4EAMgh2BKAA== + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '25' + Content-Type: + - application/x-protobuf + User-Agent: + - OTel-OpAMP-Python/0.1b0.dev + method: POST + uri: https://localhost:4320/v1/opamp + response: + body: + string: !!binary | + ChABnL5b5k5046/FazbAJ7r4GioKBgoECgASABIg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZ + G3hSuFVSAA== + headers: + Content-Length: + - '64' + Content-Type: + - application/x-protobuf + Date: + - Thu, 05 Mar 2026 14:17:01 GMT + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/conftest.py b/opamp/opentelemetry-opamp-client/tests/opamp/conftest.py similarity index 53% rename from instrumentation/opentelemetry-instrumentation-boto/tests/conftest.py rename to opamp/opentelemetry-opamp-client/tests/opamp/conftest.py index 884c6753c1..790f97f8e5 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/tests/conftest.py +++ b/opamp/opentelemetry-opamp-client/tests/opamp/conftest.py @@ -12,20 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -from os import environ +import pytest -def pytest_sessionstart(session): - # pylint: disable=unused-argument - environ["AWS_ACCESS_KEY_ID"] = "testing" - environ["AWS_SECRET_ACCESS_KEY"] = "testing" - environ["AWS_SECURITY_TOKEN"] = "testing" - environ["AWS_SESSION_TOKEN"] = "testing" +@pytest.fixture(scope="module") +def vcr_config(): + return { + "filter_headers": [ + ("authorization", "Bearer key"), + ], + "decode_compressed_response": True, + "before_record_response": scrub_response_headers, + } -def pytest_sessionfinish(session): - # pylint: disable=unused-argument - environ.pop("AWS_ACCESS_KEY_ID") - environ.pop("AWS_SECRET_ACCESS_KEY") - environ.pop("AWS_SECURITY_TOKEN") - environ.pop("AWS_SESSION_TOKEN") +def scrub_response_headers(response): + """ + This scrubs sensitive response headers. Note they are case-sensitive! + """ + return response diff --git a/opamp/opentelemetry-opamp-client/tests/opamp/test_agent.py b/opamp/opentelemetry-opamp-client/tests/opamp/test_agent.py new file mode 100644 index 0000000000..e66598df73 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/tests/opamp/test_agent.py @@ -0,0 +1,408 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from time import sleep +from unittest import mock + +from opentelemetry._opamp.agent import OpAMPAgent, _safe_invoke +from opentelemetry._opamp.agent import _Job as Job +from opentelemetry._opamp.callbacks import MessageData, OpAMPCallbacks +from opentelemetry._opamp.proto import opamp_pb2 + + +class _NoOpCallbacks(OpAMPCallbacks): + pass + + +def test_can_instantiate_agent(): + agent = OpAMPAgent( + interval=30, client=mock.Mock(), callbacks=_NoOpCallbacks() + ) + assert isinstance(agent, OpAMPAgent) + + +def test_can_start_agent(): + agent = OpAMPAgent( + interval=30, client=mock.Mock(), callbacks=_NoOpCallbacks() + ) + agent.start() + agent.stop() + + +def test_agent_start_will_send_connection_and_disconnetion_messages(): + client_mock = mock.Mock() + mock_message = mock.Mock() + mock_message.HasField.return_value = False + mock_message.flags = 0 + client_mock.send.return_value = mock_message + + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + agent = OpAMPAgent(interval=30, client=client_mock, callbacks=cb) + agent.start() + # wait for the queue to be consumed + sleep(0.1) + agent.stop() + + # one send for connection message, one for disconnect agent message + assert client_mock.send.call_count == 2 + # connection callback has been called + assert agent._schedule is True + # on_connect and on_message called for the connection response + cb.on_connect.assert_called_once_with(agent, client_mock) + cb.on_message.assert_called_once_with( + agent, client_mock, MessageData(remote_config=None) + ) + + +def test_agent_can_call_agent_stop_multiple_times(): + agent = OpAMPAgent( + interval=30, client=mock.Mock(), callbacks=_NoOpCallbacks() + ) + agent.start() + agent.stop() + agent.stop() + + +def test_agent_can_call_agent_stop_before_start(): + agent = OpAMPAgent( + interval=30, client=mock.Mock(), callbacks=_NoOpCallbacks() + ) + agent.stop() + + +def test_agent_send_warns_without_worker_thread(caplog): + agent = OpAMPAgent( + interval=30, client=mock.Mock(), callbacks=_NoOpCallbacks() + ) + agent.send(payload="payload") + + assert caplog.record_tuples == [ + ( + "opentelemetry._opamp.agent", + logging.WARNING, + "Called send() but worker thread is not alive. Worker threads is started with start()", + ) + ] + + +def test_agent_retries_before_max_attempts(caplog): + caplog.set_level(logging.DEBUG, logger="opentelemetry._opamp.agent") + + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + client_mock = mock.Mock() + connection_message = mock.Mock() + connection_message.HasField.return_value = False + connection_message.flags = 0 + server_message = mock.Mock() + server_message.HasField.return_value = False + server_message.flags = 0 + disconnection_message = mock.Mock() + client_mock.send.side_effect = [ + connection_message, + Exception("fail"), + server_message, + disconnection_message, + ] + agent = OpAMPAgent( + interval=30, + client=client_mock, + callbacks=cb, + initial_backoff=0, + ) + agent.start() + agent.send(payload="payload") + # wait for the queue to be consumed + sleep(0.1) + agent.stop() + + assert client_mock.send.call_count == 4 + assert cb.on_message.call_count == 2 + assert cb.on_connect.call_count == 2 + assert cb.on_connect_failed.call_count == 1 + + +def test_agent_stops_after_max_attempts(caplog): + caplog.set_level(logging.DEBUG, logger="opentelemetry._opamp.agent") + + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + client_mock = mock.Mock() + connection_message = mock.Mock() + connection_message.HasField.return_value = False + connection_message.flags = 0 + disconnection_message = mock.Mock() + exc1 = Exception("fail1") + exc2 = Exception("fail2") + client_mock.send.side_effect = [ + connection_message, + exc1, + exc2, + disconnection_message, + ] + agent = OpAMPAgent( + interval=30, + client=client_mock, + callbacks=cb, + max_retries=1, + initial_backoff=0, + ) + agent.start() + agent.send(payload="payload") + # wait for the queue to be consumed + sleep(0.1) + agent.stop() + + assert client_mock.send.call_count == 4 + assert cb.on_message.call_count == 1 + assert cb.on_connect_failed.call_count == 2 + cb.on_connect_failed.assert_any_call(agent, client_mock, exc1) + cb.on_connect_failed.assert_any_call(agent, client_mock, exc2) + + +def test_agent_send_enqueues_job(): + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + client_mock = mock.Mock() + msg = mock.Mock() + msg.HasField.return_value = False + msg.flags = 0 + client_mock.send.return_value = msg + + agent = OpAMPAgent(interval=30, client=client_mock, callbacks=cb) + agent.start() + # wait for the queue to be consumed + sleep(0.1) + # on_message called for connection message + assert cb.on_message.call_count == 1 + agent.send(payload="payload") + # wait for the queue to be consumed + sleep(0.1) + agent.stop() + + # on_message called once for connection and once for our message + assert cb.on_message.call_count == 2 + + +def test_on_error_called_without_on_message_for_error_response(): + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + client_mock = mock.Mock() + + error_response = opamp_pb2.ServerErrorResponse( + error_message="server error", + ) + server_msg = opamp_pb2.ServerToAgent( + error_response=error_response, + ) + # connection message (no error) + conn_msg = opamp_pb2.ServerToAgent() + + client_mock.send.side_effect = [ + conn_msg, # connection + server_msg, # message with error_response + mock.Mock(), # disconnect + ] + agent = OpAMPAgent(interval=30, client=client_mock, callbacks=cb) + agent.start() + agent.send(payload="payload") + sleep(0.1) + agent.stop() + + # on_message called only for connection (not for error_response message) + assert cb.on_message.call_count == 1 + # on_error called for the message with error_response + cb.on_error.assert_called_once_with(agent, client_mock, error_response) + + +def test_on_error_not_called_without_error_response(): + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + client_mock = mock.Mock() + + server_msg = opamp_pb2.ServerToAgent() + client_mock.send.side_effect = [ + server_msg, # connection + server_msg, # message without error_response + mock.Mock(), # disconnect + ] + agent = OpAMPAgent(interval=30, client=client_mock, callbacks=cb) + agent.start() + agent.send(payload="payload") + sleep(0.1) + agent.stop() + + assert cb.on_message.call_count == 2 + cb.on_error.assert_not_called() + + +def test_dispatch_order_with_error(): + """Verify that error_response skips on_message: on_connect -> on_error.""" + call_order = [] + client_mock = mock.Mock() + + error_response = opamp_pb2.ServerErrorResponse( + error_message="err", + ) + server_msg = opamp_pb2.ServerToAgent( + error_response=error_response, + ) + + class OrderTrackingCallbacks(OpAMPCallbacks): + def on_connect(self, agent, client): + call_order.append("on_connect") + + def on_message(self, agent, client, message): + call_order.append("on_message") + + def on_error(self, agent, client, error_response): + call_order.append("on_error") + + client_mock.send.side_effect = [ + server_msg, # connection message with error + mock.Mock(), # disconnect + ] + agent = OpAMPAgent( + interval=30, client=client_mock, callbacks=OrderTrackingCallbacks() + ) + agent.start() + sleep(0.1) + agent.stop() + + assert call_order == ["on_connect", "on_error"] + + +def test_dispatch_order_without_error(): + """Verify normal dispatch order: on_connect -> on_message.""" + call_order = [] + client_mock = mock.Mock() + + server_msg = opamp_pb2.ServerToAgent() + + class OrderTrackingCallbacks(OpAMPCallbacks): + def on_connect(self, agent, client): + call_order.append("on_connect") + + def on_message(self, agent, client, message): + call_order.append("on_message") + + def on_error(self, agent, client, error_response): + call_order.append("on_error") + + client_mock.send.side_effect = [ + server_msg, # connection message, no error + mock.Mock(), # disconnect + ] + agent = OpAMPAgent( + interval=30, client=client_mock, callbacks=OrderTrackingCallbacks() + ) + agent.start() + sleep(0.1) + agent.stop() + + assert call_order == ["on_connect", "on_message"] + + +def test_report_full_state_flag_triggers_full_state_send(): + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + client_mock = mock.Mock() + + conn_msg = opamp_pb2.ServerToAgent() + flag_msg = opamp_pb2.ServerToAgent( + flags=opamp_pb2.ServerToAgentFlags_ReportFullState, + ) + + no_flag_msg = opamp_pb2.ServerToAgent() + client_mock.send.side_effect = [ + conn_msg, # connection + flag_msg, # response with ReportFullState + no_flag_msg, # full state response + no_flag_msg, # disconnect + ] + client_mock.build_full_state_message.return_value = b"full-state" + + agent = OpAMPAgent(interval=30, client=client_mock, callbacks=cb) + agent.start() + agent.send(payload="payload") + sleep(0.2) + agent.stop() + + client_mock.build_full_state_message.assert_called() + + +def test_safe_invoke_logs_error(caplog): + caplog.set_level(logging.ERROR, logger="opentelemetry._opamp.agent") + + def bad_callback(): + raise ValueError("boom") + + _safe_invoke(bad_callback) + + assert any( + "Error when invoking function 'bad_callback'" in record.message + for record in caplog.records + ) + + +def test_safe_invoke_does_not_propagate(): + def bad_callback(): + raise RuntimeError("should not propagate") + + # Should not raise + _safe_invoke(bad_callback) + + +def test_can_instantiate_job(): + job = Job(payload="payload") + + assert isinstance(job, Job) + + +def test_job_should_retry(): + job = Job(payload="payload") + assert job.attempt == 0 + assert job.max_retries == 1 + assert job.should_retry() is True + + job.attempt += 1 + assert job.should_retry() is True + + job.attempt += 1 + assert job.should_retry() is False + + +def test_job_delay(): + job = Job(payload="payload") + + assert job.initial_backoff == 1 + job.attempt = 1 + assert ( + job.initial_backoff * 0.8 <= job.delay() <= job.initial_backoff * 1.2 + ) + + job.attempt = 2 + assert ( + 2 * job.initial_backoff * 0.8 + <= job.delay() + <= 2 * job.initial_backoff * 1.2 + ) + + job.attempt = 3 + assert ( + (2**2) * job.initial_backoff * 0.8 + <= job.delay() + <= (2**2) * job.initial_backoff * 1.2 + ) + + +def test_job_delay_has_jitter(): + job = Job(payload="payload") + job.attempt = 1 + assert len({job.delay() for i in range(10)}) > 1 diff --git a/opamp/opentelemetry-opamp-client/tests/opamp/test_callbacks.py b/opamp/opentelemetry-opamp-client/tests/opamp/test_callbacks.py new file mode 100644 index 0000000000..9493155af0 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/tests/opamp/test_callbacks.py @@ -0,0 +1,36 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import mock + +from opentelemetry._opamp.callbacks import MessageData, OpAMPCallbacks +from opentelemetry._opamp.proto import opamp_pb2 + + +def test_subclass_override_subset(): + class MyCallbacks(OpAMPCallbacks): + def __init__(self): + self.connected = False + + def on_connect(self, agent, client): + self.connected = True + + cb = MyCallbacks() + cb.on_connect(mock.Mock(), mock.Mock()) + assert cb.connected is True + + # non-overridden methods still work as no-ops + cb.on_connect_failed(mock.Mock(), mock.Mock(), Exception()) + cb.on_message(mock.Mock(), mock.Mock(), MessageData()) + cb.on_error(mock.Mock(), mock.Mock(), opamp_pb2.ServerErrorResponse()) diff --git a/opamp/opentelemetry-opamp-client/tests/opamp/test_client.py b/opamp/opentelemetry-opamp-client/tests/opamp/test_client.py new file mode 100644 index 0000000000..f67c180893 --- /dev/null +++ b/opamp/opentelemetry-opamp-client/tests/opamp/test_client.py @@ -0,0 +1,446 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=no-name-in-module + +import json +from unittest import mock + +import pytest + +from opentelemetry._opamp import messages +from opentelemetry._opamp.client import _HANDLED_CAPABILITIES, OpAMPClient +from opentelemetry._opamp.exceptions import ( + OpAMPRemoteConfigDecodeException, + OpAMPRemoteConfigParseException, +) +from opentelemetry._opamp.proto import opamp_pb2 +from opentelemetry._opamp.proto.anyvalue_pb2 import ( + AnyValue as PB2AnyValue, +) +from opentelemetry._opamp.proto.anyvalue_pb2 import ( + KeyValue as PB2KeyValue, +) +from opentelemetry._opamp.transport.requests import RequestsTransport +from opentelemetry._opamp.version import __version__ + + +@pytest.fixture(name="client") +def client_fixture(): + return OpAMPClient( + endpoint="url", agent_identifying_attributes={"foo": "bar"} + ) + + +def test_can_instantiate_opamp_client_with_defaults(): + client = OpAMPClient( + endpoint="url", agent_identifying_attributes={"foo": "bar"} + ) + + assert client + assert client._headers == { + "Content-Type": "application/x-protobuf", + "User-Agent": "OTel-OpAMP-Python/" + __version__, + } + assert client._tls_certificate is True + assert client._tls_client_certificate is None + assert client._tls_client_key is None + assert client._timeout_millis == 1_000 + assert client._sequence_num == 0 + assert isinstance(client._instance_uid, bytes) + assert isinstance(client._agent_description, opamp_pb2.AgentDescription) + assert client._agent_description.identifying_attributes == [ + PB2KeyValue(key="foo", value=PB2AnyValue(string_value="bar")), + ] + assert client._agent_description.non_identifying_attributes == [] + + +def test_can_instantiate_opamp_client_all_params(): + transport = RequestsTransport() + client = OpAMPClient( + endpoint="url", + headers={"an": "header"}, + timeout_millis=2_000, + agent_identifying_attributes={"foo": "bar"}, + agent_non_identifying_attributes={"bar": "baz"}, + transport=transport, + tls_certificate="ca.pem", + tls_client_certificate="client.pem", + tls_client_key="client-key.pem", + ) + + assert client + assert client._headers == { + "Content-Type": "application/x-protobuf", + "User-Agent": "OTel-OpAMP-Python/" + __version__, + "an": "header", + } + assert client._tls_certificate == "ca.pem" + assert client._tls_client_certificate == "client.pem" + assert client._tls_client_key == "client-key.pem" + assert client._timeout_millis == 2_000 + assert client._sequence_num == 0 + assert isinstance(client._instance_uid, bytes) + assert isinstance(client._agent_description, opamp_pb2.AgentDescription) + assert client._agent_description.identifying_attributes == [ + PB2KeyValue(key="foo", value=PB2AnyValue(string_value="bar")), + ] + assert client._agent_description.non_identifying_attributes == [ + PB2KeyValue(key="bar", value=PB2AnyValue(string_value="baz")), + ] + assert client._transport is transport + + +def test_client_headers_override_defaults(): + client = OpAMPClient( + endpoint="url", + agent_identifying_attributes={"foo": "bar"}, + headers={"User-Agent": "Custom"}, + ) + client._transport = mock.Mock() + client.send(b"") + + (send_call,) = client._transport.mock_calls + assert send_call == mock.call.send( + url="url", + headers={ + "Content-Type": "application/x-protobuf", + "User-Agent": "Custom", + }, + data=b"", + timeout_millis=1000, + tls_certificate=True, + tls_client_certificate=None, + tls_client_key=None, + ) + + +def test_can_serialize_agent_identifying_attributes(): + client = OpAMPClient( + endpoint="url", + agent_identifying_attributes={ + "string": "s", + "bytes": b"b", + "none": None, + "bool": True, + "int": 1, + "float": 2.0, + }, + ) + data = client.build_full_state_message() + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.instance_uid == client._instance_uid + assert message.sequence_num == 0 + assert message.agent_description.identifying_attributes == [ + PB2KeyValue(key="string", value=PB2AnyValue(string_value="s")), + PB2KeyValue(key="bytes", value=PB2AnyValue(bytes_value=b"b")), + PB2KeyValue(key="none", value=PB2AnyValue()), + PB2KeyValue(key="bool", value=PB2AnyValue(bool_value=True)), + PB2KeyValue(key="int", value=PB2AnyValue(int_value=1)), + PB2KeyValue(key="float", value=PB2AnyValue(double_value=2.0)), + ] + assert message.agent_description.non_identifying_attributes == [] + assert message.capabilities == _HANDLED_CAPABILITIES + + +def test_build_agent_disconnect_message(client): + data = client.build_agent_disconnect_message() + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.instance_uid == client._instance_uid + assert message.sequence_num == 0 + assert message.agent_disconnect == opamp_pb2.AgentDisconnect() + assert message.capabilities == _HANDLED_CAPABILITIES + + +def test_build_heartbeat_message(client): + data = client.build_heartbeat_message() + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.instance_uid == client._instance_uid + assert message.sequence_num == 0 + assert message.capabilities == _HANDLED_CAPABILITIES + + +def test_update_remote_config_status_without_previous_config(client): + remote_config_status = client.update_remote_config_status( + remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_APPLIED, + ) + + assert remote_config_status is not None + assert remote_config_status.last_remote_config_hash == b"12345678" + assert ( + remote_config_status.status == opamp_pb2.RemoteConfigStatuses_APPLIED + ) + assert remote_config_status.error_message == "" + + +def test_update_remote_config_status_with_same_config(client): + remote_config_status = client.update_remote_config_status( + remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_APPLIED, + ) + + assert remote_config_status is not None + + remote_config_status = client.update_remote_config_status( + remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_APPLIED, + ) + + assert remote_config_status is None + + +def test_update_remote_config_status_with_diffent_config(client): + remote_config_status = client.update_remote_config_status( + remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_APPLIED, + ) + + assert remote_config_status is not None + + # different status + remote_config_status = client.update_remote_config_status( + remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_FAILED, + ) + + assert remote_config_status is not None + + # different error message + remote_config_status = client.update_remote_config_status( + remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_FAILED, + error_message="different error message", + ) + + assert remote_config_status is not None + + # different hash + remote_config_status = client.update_remote_config_status( + remote_config_hash=b"1234", + status=opamp_pb2.RemoteConfigStatuses_FAILED, + error_message="different error message", + ) + + assert remote_config_status is not None + + +def test_build_remote_config_status_response_message_no_error_message(client): + remote_config_status = messages.build_remote_config_status_message( + last_remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_APPLIED, + ) + data = client.build_remote_config_status_response_message( + remote_config_status + ) + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.instance_uid == client._instance_uid + assert message.sequence_num == 0 + assert message.capabilities == _HANDLED_CAPABILITIES + assert message.remote_config_status + assert message.remote_config_status.last_remote_config_hash == b"12345678" + assert ( + message.remote_config_status.status + == opamp_pb2.RemoteConfigStatuses_APPLIED + ) + assert not message.remote_config_status.error_message + + +def test_build_remote_config_status_response_message_with_error_message( + client, +): + remote_config_status = messages.build_remote_config_status_message( + last_remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_FAILED, + error_message="an error message", + ) + data = client.build_remote_config_status_response_message( + remote_config_status + ) + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.instance_uid == client._instance_uid + assert message.sequence_num == 0 + assert message.capabilities == _HANDLED_CAPABILITIES + assert message.remote_config_status + assert message.remote_config_status.last_remote_config_hash == b"12345678" + assert ( + message.remote_config_status.status + == opamp_pb2.RemoteConfigStatuses_FAILED + ) + assert message.remote_config_status.error_message == "an error message" + + +def test_update_effective_config_json_content_type(client): + config = {"filename": {"a": "config"}} + client.update_effective_config(config, content_type="application/json") + + assert isinstance(client._effective_config, opamp_pb2.EffectiveConfig) + + decoded_config = {} + for ( + file_name, + config_file, + ) in client._effective_config.config_map.config_map.items(): + body = config_file.body.decode() + decoded_config[file_name] = json.loads(body) + + assert config == decoded_config + + +def test_build_full_state_message(client): + client.update_remote_config_status( + remote_config_hash=b"12345678", + status=opamp_pb2.RemoteConfigStatuses_APPLIED, + ) + config = {"filename": {"a": "config"}} + client.update_effective_config(config, content_type="application/json") + + data = client.build_full_state_message() + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.instance_uid == client._instance_uid + assert message.sequence_num == 0 + assert message.capabilities == _HANDLED_CAPABILITIES + assert message.agent_description.identifying_attributes == [ + PB2KeyValue(key="foo", value=PB2AnyValue(string_value="bar")), + ] + assert message.remote_config_status + assert message.remote_config_status.last_remote_config_hash == b"12345678" + assert ( + message.remote_config_status.status + == opamp_pb2.RemoteConfigStatuses_APPLIED + ) + assert "filename" in message.effective_config.config_map.config_map + config_file = message.effective_config.config_map.config_map["filename"] + assert config_file.content_type == "application/json" + body = config_file.body.decode() + assert config["filename"] == json.loads(body) + + +def test_build_full_state_message_no_config(client): + data = client.build_full_state_message() + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.instance_uid == client._instance_uid + assert message.sequence_num == 0 + assert message.capabilities == _HANDLED_CAPABILITIES + assert message.agent_description.identifying_attributes == [ + PB2KeyValue(key="foo", value=PB2AnyValue(string_value="bar")), + ] + assert message.remote_config_status + assert message.remote_config_status.last_remote_config_hash == b"" + assert ( + message.remote_config_status.status + == opamp_pb2.RemoteConfigStatuses_UNSET + ) + assert message.effective_config.config_map.config_map == {} + + +def test_message_sequence_num_increases_in_send(client): + client._transport = mock.Mock() + for index in range(2): + data = client.build_heartbeat_message() + client.send(data) + + message = opamp_pb2.AgentToServer() + message.ParseFromString(data) + + assert message + assert message.sequence_num == index + + +def test_send(client): + client._transport = mock.Mock() + client.send(b"foo") + + (send_call,) = client._transport.mock_calls + assert send_call == mock.call.send( + url="url", + headers={ + "Content-Type": "application/x-protobuf", + "User-Agent": "OTel-OpAMP-Python/" + __version__, + }, + data=b"foo", + timeout_millis=1000, + tls_certificate=True, + tls_client_certificate=None, + tls_client_key=None, + ) + + +def test_decode_remote_config(client): + config = opamp_pb2.AgentConfigMap() + config.config_map["application/json"].body = json.dumps( + {"a": "config"} + ).encode() + config.config_map["application/json"].content_type = "application/json" + config.config_map["text/json"].body = json.dumps( + {"other": "config"} + ).encode() + config.config_map["text/json"].content_type = "text/json" + message = opamp_pb2.AgentRemoteConfig(config=config) + + decoded = list(client.decode_remote_config(message)) + assert sorted(decoded) == sorted( + [ + ("application/json", {"a": "config"}), + ("text/json", {"other": "config"}), + ] + ) + + +def test_decode_remote_config_invalid_content_type(client): + config = opamp_pb2.AgentConfigMap() + config.config_map["filename"].body = b"1" + config.config_map["filename"].content_type = "not/json" + message = opamp_pb2.AgentRemoteConfig(config=config) + + with pytest.raises(OpAMPRemoteConfigParseException): + list(client.decode_remote_config(message)) + + +def test_decode_remote_config_invalid_file_body(client): + config = opamp_pb2.AgentConfigMap() + config.config_map["filename"].body = b"notjson" + config.config_map["filename"].content_type = "application/json" + message = opamp_pb2.AgentRemoteConfig(config=config) + + with pytest.raises(OpAMPRemoteConfigDecodeException): + list(client.decode_remote_config(message)) diff --git a/opamp/opentelemetry-opamp-client/tests/opamp/test_e2e.py b/opamp/opentelemetry-opamp-client/tests/opamp/test_e2e.py new file mode 100644 index 0000000000..745fb63faa --- /dev/null +++ b/opamp/opentelemetry-opamp-client/tests/opamp/test_e2e.py @@ -0,0 +1,117 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from time import sleep +from unittest import mock + +import pytest + +from opentelemetry._opamp.agent import OpAMPAgent +from opentelemetry._opamp.callbacks import OpAMPCallbacks +from opentelemetry._opamp.client import OpAMPClient +from opentelemetry._opamp.proto import opamp_pb2 + + +@pytest.mark.vcr() +def test_connection_remote_config_status_heartbeat_disconnection(caplog): + caplog.set_level(logging.DEBUG, logger="opentelemetry._opamp.agent") + + class E2ECallbacks(OpAMPCallbacks): + def on_message(self, agent, client, message): + logger = logging.getLogger( + "opentelemetry._opamp.agent.opamp_handler" + ) + + logger.debug("In opamp_handler") + + # we need to update the config only if we have a config + if ( + message.remote_config is None + or not message.remote_config.config_hash + ): + return + + updated_remote_config = client.update_remote_config_status( + remote_config_hash=message.remote_config.config_hash, + status=opamp_pb2.RemoteConfigStatuses_APPLIED, + error_message="", + ) + if updated_remote_config is not None: + logger.debug("Updated Remote Config") + msg = client.build_remote_config_status_response_message( + updated_remote_config + ) + agent.send(payload=msg) + + opamp_client = OpAMPClient( + endpoint="https://localhost:4320/v1/opamp", + agent_identifying_attributes={ + "service.name": "foo", + "deployment.environment.name": "foo", + }, + tls_certificate=False, + ) + opamp_agent = OpAMPAgent( + interval=1, + callbacks=E2ECallbacks(), + client=opamp_client, + ) + opamp_agent.start() + + # this should be enough for the heartbeat message to be sent + sleep(1.5) + + opamp_agent.stop() + + handler_records = [ + record[2] + for record in caplog.record_tuples + if record[0] == "opentelemetry._opamp.agent.opamp_handler" + ] + # connection response has ReportFullState flag, triggering a full state send. + # on_message is called for: connection, full state response, config status response, heartbeat. + assert handler_records == [ + "In opamp_handler", + "Updated Remote Config", + "In opamp_handler", + "In opamp_handler", + "In opamp_handler", + ] + + +@pytest.mark.vcr() +def test_with_server_not_responding(caplog): + caplog.set_level(logging.DEBUG, logger="opentelemetry._opamp.agent") + + cb = mock.create_autospec(OpAMPCallbacks, instance=True) + + opamp_client = OpAMPClient( + endpoint="https://localhost:4399/v1/opamp", + agent_identifying_attributes={ + "service.name": "foo", + "deployment.environment.name": "foo", + }, + tls_certificate=False, + ) + opamp_agent = OpAMPAgent( + interval=1, + callbacks=cb, + client=opamp_client, + ) + opamp_agent.start() + + opamp_agent.stop() + + assert cb.on_message.call_count == 0 diff --git a/opamp/opentelemetry-opamp-client/tests/opamp/transport/test_requests.py b/opamp/opentelemetry-opamp-client/tests/opamp/transport/test_requests.py new file mode 100644 index 0000000000..ff462554ca --- /dev/null +++ b/opamp/opentelemetry-opamp-client/tests/opamp/transport/test_requests.py @@ -0,0 +1,178 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import mock + +import pytest +import requests + +from opentelemetry._opamp.proto import opamp_pb2 +from opentelemetry._opamp.transport.base import base_headers +from opentelemetry._opamp.transport.exceptions import OpAMPException +from opentelemetry._opamp.transport.requests import RequestsTransport + + +def test_can_instantiate_requests_transport(): + transport = RequestsTransport() + + assert transport + + +def test_can_instantiate_requests_transport_with_own_session(): + session = requests.Session() + transport = RequestsTransport(session=session) + + assert transport + assert transport.session is session + + +def test_can_send(): + transport = RequestsTransport() + serialized_message = opamp_pb2.ServerToAgent().SerializeToString() + response_mock = mock.Mock(content=serialized_message) + headers = {"foo": "bar"} + expected_headers = {**base_headers, **headers} + data = b"" + with mock.patch.object(transport, "session") as session_mock: + session_mock.post.return_value = response_mock + response = transport.send( + url="http://127.0.0.1/v1/opamp", + headers=headers, + data=data, + timeout_millis=1_000, + tls_certificate=True, + ) + + session_mock.post.assert_called_once_with( + "http://127.0.0.1/v1/opamp", + headers=expected_headers, + data=data, + timeout=1, + verify=True, + cert=None, + ) + + assert isinstance(response, opamp_pb2.ServerToAgent) + + +def test_send_tls_certificate_mapped_to_verify(): + transport = RequestsTransport() + serialized_message = opamp_pb2.ServerToAgent().SerializeToString() + response_mock = mock.Mock(content=serialized_message) + data = b"" + with mock.patch.object(transport, "session") as session_mock: + session_mock.post.return_value = response_mock + response = transport.send( + url="https://127.0.0.1/v1/opamp", + headers={}, + data=data, + timeout_millis=1_000, + tls_certificate=False, + ) + + session_mock.post.assert_called_once_with( + "https://127.0.0.1/v1/opamp", + headers=base_headers, + data=data, + timeout=1, + verify=False, + cert=None, + ) + + assert isinstance(response, opamp_pb2.ServerToAgent) + + +def test_send_mtls(): + transport = RequestsTransport() + serialized_message = opamp_pb2.ServerToAgent().SerializeToString() + response_mock = mock.Mock(content=serialized_message) + data = b"" + with mock.patch.object(transport, "session") as session_mock: + session_mock.post.return_value = response_mock + response = transport.send( + url="https://127.0.0.1/v1/opamp", + headers={}, + data=data, + timeout_millis=1_000, + tls_certificate="server.pem", + tls_client_certificate="client.pem", + tls_client_key="client.key", + ) + + session_mock.post.assert_called_once_with( + "https://127.0.0.1/v1/opamp", + headers=base_headers, + data=data, + timeout=1, + verify="server.pem", + cert=("client.pem", "client.key"), + ) + + assert isinstance(response, opamp_pb2.ServerToAgent) + + +def test_send_mtls_no_client_key(): + transport = RequestsTransport() + serialized_message = opamp_pb2.ServerToAgent().SerializeToString() + response_mock = mock.Mock(content=serialized_message) + data = b"" + with mock.patch.object(transport, "session") as session_mock: + session_mock.post.return_value = response_mock + response = transport.send( + url="https://127.0.0.1/v1/opamp", + headers={}, + data=data, + timeout_millis=1_000, + tls_certificate="server.pem", + tls_client_certificate="client.pem", + ) + + session_mock.post.assert_called_once_with( + "https://127.0.0.1/v1/opamp", + headers=base_headers, + data=data, + timeout=1, + verify="server.pem", + cert="client.pem", + ) + + assert isinstance(response, opamp_pb2.ServerToAgent) + + +def test_send_exceptions_raises_opamp_exception(): + transport = RequestsTransport() + response_mock = mock.Mock() + headers = {"foo": "bar"} + expected_headers = {**base_headers, **headers} + data = b"" + with mock.patch.object(transport, "session") as session_mock: + session_mock.post.return_value = response_mock + response_mock.raise_for_status.side_effect = Exception + with pytest.raises(OpAMPException): + transport.send( + url="http://127.0.0.1/v1/opamp", + headers=headers, + data=data, + timeout_millis=1_000, + tls_certificate=True, + ) + + session_mock.post.assert_called_once_with( + "http://127.0.0.1/v1/opamp", + headers=expected_headers, + data=data, + timeout=1, + verify=True, + cert=None, + ) diff --git a/opentelemetry-contrib-instrumentations/pyproject.toml b/opentelemetry-contrib-instrumentations/pyproject.toml index 0b4a0c8398..630509a4b2 100644 --- a/opentelemetry-contrib-instrumentations/pyproject.toml +++ b/opentelemetry-contrib-instrumentations/pyproject.toml @@ -12,7 +12,7 @@ dynamic = [ description = "OpenTelemetry Contrib Instrumentation Packages" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -22,7 +22,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -30,57 +29,56 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-instrumentation-aio-pika==0.62b0.dev", - "opentelemetry-instrumentation-aiohttp-client==0.62b0.dev", - "opentelemetry-instrumentation-aiohttp-server==0.62b0.dev", - "opentelemetry-instrumentation-aiokafka==0.62b0.dev", - "opentelemetry-instrumentation-aiopg==0.62b0.dev", - "opentelemetry-instrumentation-asgi==0.62b0.dev", - "opentelemetry-instrumentation-asyncclick==0.62b0.dev", - "opentelemetry-instrumentation-asyncio==0.62b0.dev", - "opentelemetry-instrumentation-asyncpg==0.62b0.dev", - "opentelemetry-instrumentation-aws-lambda==0.62b0.dev", - "opentelemetry-instrumentation-boto==0.62b0.dev", - "opentelemetry-instrumentation-boto3sqs==0.62b0.dev", - "opentelemetry-instrumentation-botocore==0.62b0.dev", - "opentelemetry-instrumentation-cassandra==0.62b0.dev", - "opentelemetry-instrumentation-celery==0.62b0.dev", - "opentelemetry-instrumentation-click==0.62b0.dev", - "opentelemetry-instrumentation-confluent-kafka==0.62b0.dev", - "opentelemetry-instrumentation-dbapi==0.62b0.dev", - "opentelemetry-instrumentation-django==0.62b0.dev", - "opentelemetry-instrumentation-elasticsearch==0.62b0.dev", - "opentelemetry-instrumentation-falcon==0.62b0.dev", - "opentelemetry-instrumentation-fastapi==0.62b0.dev", - "opentelemetry-instrumentation-flask==0.62b0.dev", - "opentelemetry-instrumentation-grpc==0.62b0.dev", - "opentelemetry-instrumentation-httpx==0.62b0.dev", - "opentelemetry-instrumentation-jinja2==0.62b0.dev", - "opentelemetry-instrumentation-kafka-python==0.62b0.dev", - "opentelemetry-instrumentation-logging==0.62b0.dev", - "opentelemetry-instrumentation-mysql==0.62b0.dev", - "opentelemetry-instrumentation-mysqlclient==0.62b0.dev", - "opentelemetry-instrumentation-pika==0.62b0.dev", - "opentelemetry-instrumentation-psycopg==0.62b0.dev", - "opentelemetry-instrumentation-psycopg2==0.62b0.dev", - "opentelemetry-instrumentation-pymemcache==0.62b0.dev", - "opentelemetry-instrumentation-pymongo==0.62b0.dev", - "opentelemetry-instrumentation-pymssql==0.62b0.dev", - "opentelemetry-instrumentation-pymysql==0.62b0.dev", - "opentelemetry-instrumentation-pyramid==0.62b0.dev", - "opentelemetry-instrumentation-redis==0.62b0.dev", - "opentelemetry-instrumentation-remoulade==0.62b0.dev", - "opentelemetry-instrumentation-requests==0.62b0.dev", - "opentelemetry-instrumentation-sqlalchemy==0.62b0.dev", - "opentelemetry-instrumentation-sqlite3==0.62b0.dev", - "opentelemetry-instrumentation-starlette==0.62b0.dev", - "opentelemetry-instrumentation-system-metrics==0.62b0.dev", - "opentelemetry-instrumentation-threading==0.62b0.dev", - "opentelemetry-instrumentation-tornado==0.62b0.dev", - "opentelemetry-instrumentation-tortoiseorm==0.62b0.dev", - "opentelemetry-instrumentation-urllib==0.62b0.dev", - "opentelemetry-instrumentation-urllib3==0.62b0.dev", - "opentelemetry-instrumentation-wsgi==0.62b0.dev", + "opentelemetry-instrumentation-aio-pika==0.63b0.dev", + "opentelemetry-instrumentation-aiohttp-client==0.63b0.dev", + "opentelemetry-instrumentation-aiohttp-server==0.63b0.dev", + "opentelemetry-instrumentation-aiokafka==0.63b0.dev", + "opentelemetry-instrumentation-aiopg==0.63b0.dev", + "opentelemetry-instrumentation-asgi==0.63b0.dev", + "opentelemetry-instrumentation-asyncclick==0.63b0.dev", + "opentelemetry-instrumentation-asyncio==0.63b0.dev", + "opentelemetry-instrumentation-asyncpg==0.63b0.dev", + "opentelemetry-instrumentation-aws-lambda==0.63b0.dev", + "opentelemetry-instrumentation-boto3sqs==0.63b0.dev", + "opentelemetry-instrumentation-botocore==0.63b0.dev", + "opentelemetry-instrumentation-cassandra==0.63b0.dev", + "opentelemetry-instrumentation-celery==0.63b0.dev", + "opentelemetry-instrumentation-click==0.63b0.dev", + "opentelemetry-instrumentation-confluent-kafka==0.63b0.dev", + "opentelemetry-instrumentation-dbapi==0.63b0.dev", + "opentelemetry-instrumentation-django==0.63b0.dev", + "opentelemetry-instrumentation-elasticsearch==0.63b0.dev", + "opentelemetry-instrumentation-falcon==0.63b0.dev", + "opentelemetry-instrumentation-fastapi==0.63b0.dev", + "opentelemetry-instrumentation-flask==0.63b0.dev", + "opentelemetry-instrumentation-grpc==0.63b0.dev", + "opentelemetry-instrumentation-httpx==0.63b0.dev", + "opentelemetry-instrumentation-jinja2==0.63b0.dev", + "opentelemetry-instrumentation-kafka-python==0.63b0.dev", + "opentelemetry-instrumentation-logging==0.63b0.dev", + "opentelemetry-instrumentation-mysql==0.63b0.dev", + "opentelemetry-instrumentation-mysqlclient==0.63b0.dev", + "opentelemetry-instrumentation-pika==0.63b0.dev", + "opentelemetry-instrumentation-psycopg==0.63b0.dev", + "opentelemetry-instrumentation-psycopg2==0.63b0.dev", + "opentelemetry-instrumentation-pymemcache==0.63b0.dev", + "opentelemetry-instrumentation-pymongo==0.63b0.dev", + "opentelemetry-instrumentation-pymssql==0.63b0.dev", + "opentelemetry-instrumentation-pymysql==0.63b0.dev", + "opentelemetry-instrumentation-pyramid==0.63b0.dev", + "opentelemetry-instrumentation-redis==0.63b0.dev", + "opentelemetry-instrumentation-remoulade==0.63b0.dev", + "opentelemetry-instrumentation-requests==0.63b0.dev", + "opentelemetry-instrumentation-sqlalchemy==0.63b0.dev", + "opentelemetry-instrumentation-sqlite3==0.63b0.dev", + "opentelemetry-instrumentation-starlette==0.63b0.dev", + "opentelemetry-instrumentation-system-metrics==0.63b0.dev", + "opentelemetry-instrumentation-threading==0.63b0.dev", + "opentelemetry-instrumentation-tornado==0.63b0.dev", + "opentelemetry-instrumentation-tortoiseorm==0.63b0.dev", + "opentelemetry-instrumentation-urllib==0.63b0.dev", + "opentelemetry-instrumentation-urllib3==0.63b0.dev", + "opentelemetry-instrumentation-wsgi==0.63b0.dev", ] [project.urls] diff --git a/opentelemetry-contrib-instrumentations/src/opentelemetry/contrib-instrumentations/version.py b/opentelemetry-contrib-instrumentations/src/opentelemetry/contrib-instrumentations/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/opentelemetry-contrib-instrumentations/src/opentelemetry/contrib-instrumentations/version.py +++ b/opentelemetry-contrib-instrumentations/src/opentelemetry/contrib-instrumentations/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/opentelemetry-distro/pyproject.toml b/opentelemetry-distro/pyproject.toml index d57eb6c31c..432ed1264d 100644 --- a/opentelemetry-distro/pyproject.toml +++ b/opentelemetry-distro/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Python Distro" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -28,13 +27,13 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.12", - "opentelemetry-instrumentation == 0.62b0.dev", + "opentelemetry-instrumentation == 0.63b0.dev", "opentelemetry-sdk ~= 1.13", ] [project.optional-dependencies] otlp = [ - "opentelemetry-exporter-otlp == 1.41.0.dev", + "opentelemetry-exporter-otlp == 1.42.0.dev", ] [project.entry-points.opentelemetry_configurator] diff --git a/opentelemetry-distro/src/opentelemetry/distro/version.py b/opentelemetry-distro/src/opentelemetry/distro/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/opentelemetry-distro/src/opentelemetry/distro/version.py +++ b/opentelemetry-distro/src/opentelemetry/distro/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/opentelemetry-distro/test-requirements.txt b/opentelemetry-distro/test-requirements.txt index dba17daec3..75e0e99298 100644 --- a/opentelemetry-distro/test-requirements.txt +++ b/opentelemetry-distro/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/opentelemetry-instrumentation/pyproject.toml b/opentelemetry-instrumentation/pyproject.toml index e628f6cd65..5a472d774e 100644 --- a/opentelemetry-instrumentation/pyproject.toml +++ b/opentelemetry-instrumentation/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,8 +26,8 @@ classifiers = [ ] dependencies = [ "opentelemetry-api ~= 1.4", - "opentelemetry-semantic-conventions == 0.62b0.dev", - "wrapt >= 1.0.0, < 2.0.0", + "opentelemetry-semantic-conventions == 0.63b0.dev", + "wrapt >= 1.0.0, < 3.0.0", "packaging >= 18.0", ] diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index e30cdf2dfb..1edd18d038 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -18,10 +18,14 @@ import threading from enum import Enum from typing import Container, Mapping, MutableMapping +from urllib.parse import urlparse + +from packaging import version as package_version from opentelemetry.instrumentation.utils import http_status_to_status_code from opentelemetry.semconv._incubating.attributes.db_attributes import ( DB_NAME, + DB_OPERATION, DB_STATEMENT, DB_SYSTEM, DB_USER, @@ -50,6 +54,7 @@ ) from opentelemetry.semconv.attributes.db_attributes import ( DB_NAMESPACE, + DB_OPERATION_NAME, DB_QUERY_TEXT, DB_SYSTEM_NAME, ) @@ -177,6 +182,9 @@ OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN" +# Legacy/default schema version when schema_url was first introduced +_LEGACY_SCHEMA_VERSION = "1.11.0" + class _OpenTelemetryStabilitySignalType(Enum): HTTP = "http" @@ -327,7 +335,7 @@ def set_int_attribute( try: result[key] = int(value) except ValueError: - return + pass def _set_http_method( @@ -590,6 +598,17 @@ def _set_db_user( # No new attribute - db.user was removed with no replacement +def _set_db_operation( + result: MutableMapping[str, AttributeValue], + operation: str, + sem_conv_opt_in_mode: _StabilityMode, +) -> None: + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, DB_OPERATION, operation) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, DB_OPERATION_NAME, operation) + + # General @@ -634,8 +653,63 @@ def _set_status( span.set_status(Status(status)) -# Get schema version based off of opt-in mode def _get_schema_url(mode: _StabilityMode) -> str: + """Get schema version URL for a single signal type's opt-in mode (backwards compatible). + + For new instrumentations using multiple signal types, use + _get_schema_url_for_signal_types() + """ if mode is _StabilityMode.DEFAULT: - return "https://opentelemetry.io/schemas/1.11.0" + return f"https://opentelemetry.io/schemas/{_LEGACY_SCHEMA_VERSION}" return Schemas.V1_21_0.value + + +def _get_schema_version_for_opt_in_mode( + signal_type: _OpenTelemetryStabilitySignalType, + mode: _StabilityMode, +) -> str: + """Get the schema version for a specific signal type and opt-in mode.""" + if mode == _StabilityMode.DEFAULT: + return _LEGACY_SCHEMA_VERSION + + signal_versions = { + _OpenTelemetryStabilitySignalType.HTTP: Schemas.V1_21_0.value, + _OpenTelemetryStabilitySignalType.DATABASE: Schemas.V1_25_0.value, + _OpenTelemetryStabilitySignalType.GEN_AI: Schemas.V1_26_0.value, + } + schema_url = signal_versions.get(signal_type) + if not schema_url: + return _LEGACY_SCHEMA_VERSION + + path = urlparse(schema_url).path + schema_version = path.rstrip("/").split("/")[-1] + return schema_version or _LEGACY_SCHEMA_VERSION + + +def _get_schema_url_for_signal_types( + signal_types: list[_OpenTelemetryStabilitySignalType], +) -> str: + """Get the highest applicable schema URL for multiple signal types. + + Note: + Instrumentors should call _OpenTelemetrySemanticConventionStability._initialize() + before using this function to ensure proper initialization of stability modes. + + Args: + signal_types: List of signal types used by the instrumentation + + Returns: + Schema URL string representing the highest applicable semconv version + """ + highest_schema_version = _LEGACY_SCHEMA_VERSION + for signal_type in signal_types: + mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + signal_type + ) + schema_version = _get_schema_version_for_opt_in_mode(signal_type, mode) + # Keep the highest for all signals + if package_version.Version(schema_version) > package_version.Version( + highest_schema_version + ): + highest_schema_version = schema_version + return f"https://opentelemetry.io/schemas/{highest_schema_version}" diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py index cc0ac68f1c..3fbee088e4 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py @@ -117,8 +117,7 @@ def _is_installed(req): def _find_installed_libraries(default_instrumentations, libraries): - for lib in default_instrumentations: - yield lib + yield from default_instrumentations for lib in libraries: if _is_installed(lib["library"]): diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py index ee73007fca..999181bd9f 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -26,199 +26,199 @@ }, { "library": "aio_pika >= 7.2.0, < 10.0.0", - "instrumentation": "opentelemetry-instrumentation-aio-pika==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-aio-pika==0.63b0.dev", }, { "library": "aiohttp ~= 3.0", - "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.63b0.dev", }, { "library": "aiohttp ~= 3.0", - "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.63b0.dev", }, { "library": "aiokafka >= 0.8, < 1.0", - "instrumentation": "opentelemetry-instrumentation-aiokafka==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-aiokafka==0.63b0.dev", }, { "library": "aiopg >= 0.13.0, < 2.0.0", - "instrumentation": "opentelemetry-instrumentation-aiopg==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-aiopg==0.63b0.dev", }, { "library": "asgiref ~= 3.0", - "instrumentation": "opentelemetry-instrumentation-asgi==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-asgi==0.63b0.dev", }, { "library": "asyncclick ~= 8.0", - "instrumentation": "opentelemetry-instrumentation-asyncclick==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-asyncclick==0.63b0.dev", }, { "library": "asyncpg >= 0.12.0", - "instrumentation": "opentelemetry-instrumentation-asyncpg==0.62b0.dev", - }, - { - "library": "boto~=2.0", - "instrumentation": "opentelemetry-instrumentation-boto==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-asyncpg==0.63b0.dev", }, { "library": "boto3 ~= 1.0", - "instrumentation": "opentelemetry-instrumentation-boto3sqs==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-boto3sqs==0.63b0.dev", }, { "library": "botocore ~= 1.0", - "instrumentation": "opentelemetry-instrumentation-botocore==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-botocore==0.63b0.dev", + }, + { + "library": "aiobotocore ~= 2.0", + "instrumentation": "opentelemetry-instrumentation-botocore==0.63b0.dev", }, { "library": "cassandra-driver ~= 3.25", - "instrumentation": "opentelemetry-instrumentation-cassandra==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-cassandra==0.63b0.dev", }, { "library": "scylla-driver ~= 3.25", - "instrumentation": "opentelemetry-instrumentation-cassandra==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-cassandra==0.63b0.dev", }, { "library": "celery >= 4.0, < 6.0", - "instrumentation": "opentelemetry-instrumentation-celery==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-celery==0.63b0.dev", }, { "library": "click >= 8.1.3, < 9.0.0", - "instrumentation": "opentelemetry-instrumentation-click==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-click==0.63b0.dev", }, { - "library": "confluent-kafka >= 1.8.2, <= 2.13.0", - "instrumentation": "opentelemetry-instrumentation-confluent-kafka==0.62b0.dev", + "library": "confluent-kafka >= 1.8.2, < 3.0.0", + "instrumentation": "opentelemetry-instrumentation-confluent-kafka==0.63b0.dev", }, { "library": "django >= 2.0", - "instrumentation": "opentelemetry-instrumentation-django==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-django==0.63b0.dev", }, { "library": "elasticsearch >= 6.0", - "instrumentation": "opentelemetry-instrumentation-elasticsearch==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-elasticsearch==0.63b0.dev", }, { "library": "falcon >= 1.4.1, < 5.0.0", - "instrumentation": "opentelemetry-instrumentation-falcon==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-falcon==0.63b0.dev", }, { "library": "fastapi ~= 0.92", - "instrumentation": "opentelemetry-instrumentation-fastapi==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-fastapi==0.63b0.dev", }, { "library": "flask >= 1.0", - "instrumentation": "opentelemetry-instrumentation-flask==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-flask==0.63b0.dev", }, { "library": "grpcio >= 1.42.0", - "instrumentation": "opentelemetry-instrumentation-grpc==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-grpc==0.63b0.dev", }, { "library": "httpx >= 0.18.0", - "instrumentation": "opentelemetry-instrumentation-httpx==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-httpx==0.63b0.dev", }, { "library": "jinja2 >= 2.7, < 4.0", - "instrumentation": "opentelemetry-instrumentation-jinja2==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-jinja2==0.63b0.dev", }, { "library": "kafka-python >= 2.0, < 3.0", - "instrumentation": "opentelemetry-instrumentation-kafka-python==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-kafka-python==0.63b0.dev", }, { "library": "kafka-python-ng >= 2.0, < 3.0", - "instrumentation": "opentelemetry-instrumentation-kafka-python==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-kafka-python==0.63b0.dev", }, { "library": "mysql-connector-python >= 8.0, < 10.0", - "instrumentation": "opentelemetry-instrumentation-mysql==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-mysql==0.63b0.dev", }, { "library": "mysqlclient < 3", - "instrumentation": "opentelemetry-instrumentation-mysqlclient==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-mysqlclient==0.63b0.dev", }, { "library": "pika >= 0.12.0", - "instrumentation": "opentelemetry-instrumentation-pika==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-pika==0.63b0.dev", }, { "library": "psycopg >= 3.1.0", - "instrumentation": "opentelemetry-instrumentation-psycopg==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-psycopg==0.63b0.dev", }, { "library": "psycopg2 >= 2.7.3.1", - "instrumentation": "opentelemetry-instrumentation-psycopg2==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-psycopg2==0.63b0.dev", }, { "library": "psycopg2-binary >= 2.7.3.1", - "instrumentation": "opentelemetry-instrumentation-psycopg2==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-psycopg2==0.63b0.dev", }, { "library": "pymemcache >= 1.3.5, < 5", - "instrumentation": "opentelemetry-instrumentation-pymemcache==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-pymemcache==0.63b0.dev", }, { "library": "pymongo >= 3.1, < 5.0", - "instrumentation": "opentelemetry-instrumentation-pymongo==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-pymongo==0.63b0.dev", }, { "library": "pymssql >= 2.1.5, < 3", - "instrumentation": "opentelemetry-instrumentation-pymssql==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-pymssql==0.63b0.dev", }, { "library": "PyMySQL < 2", - "instrumentation": "opentelemetry-instrumentation-pymysql==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-pymysql==0.63b0.dev", }, { "library": "pyramid >= 1.7", - "instrumentation": "opentelemetry-instrumentation-pyramid==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-pyramid==0.63b0.dev", }, { "library": "redis >= 2.6", - "instrumentation": "opentelemetry-instrumentation-redis==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-redis==0.63b0.dev", }, { "library": "remoulade >= 0.50", - "instrumentation": "opentelemetry-instrumentation-remoulade==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-remoulade==0.63b0.dev", }, { "library": "requests ~= 2.0", - "instrumentation": "opentelemetry-instrumentation-requests==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-requests==0.63b0.dev", }, { "library": "sqlalchemy >= 1.0.0, < 2.1.0", - "instrumentation": "opentelemetry-instrumentation-sqlalchemy==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-sqlalchemy==0.63b0.dev", }, { "library": "starlette >= 0.13", - "instrumentation": "opentelemetry-instrumentation-starlette==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-starlette==0.63b0.dev", }, { "library": "psutil >= 5", - "instrumentation": "opentelemetry-instrumentation-system-metrics==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-system-metrics==0.63b0.dev", }, { "library": "tornado >= 5.1.1", - "instrumentation": "opentelemetry-instrumentation-tornado==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-tornado==0.63b0.dev", }, { "library": "tortoise-orm >= 0.17.0", - "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.63b0.dev", }, { "library": "pydantic >= 1.10.2", - "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.63b0.dev", }, { "library": "urllib3 >= 1.0.0, < 3.0.0", - "instrumentation": "opentelemetry-instrumentation-urllib3==0.62b0.dev", + "instrumentation": "opentelemetry-instrumentation-urllib3==0.63b0.dev", }, ] default_instrumentations = [ - "opentelemetry-instrumentation-asyncio==0.62b0.dev", - "opentelemetry-instrumentation-dbapi==0.62b0.dev", - "opentelemetry-instrumentation-logging==0.62b0.dev", - "opentelemetry-instrumentation-sqlite3==0.62b0.dev", - "opentelemetry-instrumentation-threading==0.62b0.dev", - "opentelemetry-instrumentation-urllib==0.62b0.dev", - "opentelemetry-instrumentation-wsgi==0.62b0.dev", + "opentelemetry-instrumentation-asyncio==0.63b0.dev", + "opentelemetry-instrumentation-dbapi==0.63b0.dev", + "opentelemetry-instrumentation-logging==0.63b0.dev", + "opentelemetry-instrumentation-sqlite3==0.63b0.dev", + "opentelemetry-instrumentation-threading==0.63b0.dev", + "opentelemetry-instrumentation-urllib==0.63b0.dev", + "opentelemetry-instrumentation-wsgi==0.63b0.dev", ] diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py index e38932e28d..a672809fbd 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py @@ -14,13 +14,18 @@ from __future__ import annotations +import sys import urllib.parse from contextlib import contextmanager from importlib import import_module from re import escape, sub from typing import Any, Dict, Generator, Sequence -from wrapt import ObjectProxy +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy from opentelemetry import context, trace @@ -99,6 +104,9 @@ def unwrap(obj: object, attr: str): raise ImportError( f"Cannot parse '{obj}' as dotted import path" ) from exc + if module_path not in sys.modules: + # Was never imported, meaning it could never have been wrapped + return module = import_module(module_path) try: obj = getattr(module, class_name) @@ -108,7 +116,11 @@ def unwrap(obj: object, attr: str): ) from exc func = getattr(obj, attr, None) - if func and isinstance(func, ObjectProxy) and hasattr(func, "__wrapped__"): + if ( + func + and isinstance(func, BaseObjectProxy) + and hasattr(func, "__wrapped__") + ): setattr(obj, attr, func.__wrapped__) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/version.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/version.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/opentelemetry-instrumentation/test-requirements-wrapt1.txt b/opentelemetry-instrumentation/test-requirements-wrapt1.txt new file mode 100644 index 0000000000..ae191f8ee5 --- /dev/null +++ b/opentelemetry-instrumentation/test-requirements-wrapt1.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==1.17.3 diff --git a/opentelemetry-instrumentation/test-requirements-wrapt2.txt b/opentelemetry-instrumentation/test-requirements-wrapt2.txt new file mode 100644 index 0000000000..01c75bb94f --- /dev/null +++ b/opentelemetry-instrumentation/test-requirements-wrapt2.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +wrapt==2.1.2 diff --git a/opentelemetry-instrumentation/test-requirements.txt b/opentelemetry-instrumentation/test-requirements.txt index 58c09a950b..a05165dbbb 100644 --- a/opentelemetry-instrumentation/test-requirements.txt +++ b/opentelemetry-instrumentation/test-requirements.txt @@ -1,13 +1,11 @@ asgiref==3.8.1 -Deprecated==1.2.14 gevent==25.5.1 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 typing_extensions==4.12.2 -wrapt==1.16.0 zipp==3.19.2 -e opentelemetry-instrumentation diff --git a/opentelemetry-instrumentation/tests/test_semconv.py b/opentelemetry-instrumentation/tests/test_semconv.py index 4560e28101..f160c51286 100644 --- a/opentelemetry-instrumentation/tests/test_semconv.py +++ b/opentelemetry-instrumentation/tests/test_semconv.py @@ -17,10 +17,14 @@ from unittest.mock import Mock, patch from opentelemetry.instrumentation._semconv import ( + _LEGACY_SCHEMA_VERSION, OTEL_SEMCONV_STABILITY_OPT_IN, + _get_schema_url_for_signal_types, + _get_schema_version_for_opt_in_mode, _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilitySignalType, _set_db_name, + _set_db_operation, _set_db_statement, _set_db_system, _set_db_user, @@ -29,12 +33,14 @@ ) from opentelemetry.semconv._incubating.attributes.db_attributes import ( DB_NAME, + DB_OPERATION, DB_STATEMENT, DB_SYSTEM, DB_USER, ) from opentelemetry.semconv.attributes.db_attributes import ( DB_NAMESPACE, + DB_OPERATION_NAME, DB_QUERY_TEXT, DB_SYSTEM_NAME, ) @@ -188,6 +194,134 @@ def test_stability_mode_dup_precedence(self): ) +class TestOpenTelemetrySemConvSchemaUrl(TestCase): + @stability_mode("") + def test_get_schema_version_for_opt_in_mode_default(self): + version = _get_schema_version_for_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, _StabilityMode.DEFAULT + ) + self.assertEqual(version, _LEGACY_SCHEMA_VERSION) + + version = _get_schema_version_for_opt_in_mode( + _OpenTelemetryStabilitySignalType.DATABASE, _StabilityMode.DEFAULT + ) + self.assertEqual(version, _LEGACY_SCHEMA_VERSION) + + version = _get_schema_version_for_opt_in_mode( + _OpenTelemetryStabilitySignalType.GEN_AI, _StabilityMode.DEFAULT + ) + self.assertEqual(version, _LEGACY_SCHEMA_VERSION) + + @stability_mode("") + def test_get_schema_version_for_opt_in_mode_http_stable(self): + version = _get_schema_version_for_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, _StabilityMode.HTTP + ) + self.assertEqual(version, "1.21.0") + + @stability_mode("") + def test_get_schema_version_for_opt_in_mode_database_stable(self): + version = _get_schema_version_for_opt_in_mode( + _OpenTelemetryStabilitySignalType.DATABASE, _StabilityMode.DATABASE + ) + self.assertEqual(version, "1.25.0") + + @stability_mode("") + def test_get_schema_version_for_opt_in_mode_gen_ai_stable(self): + version = _get_schema_version_for_opt_in_mode( + _OpenTelemetryStabilitySignalType.GEN_AI, + _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL, + ) + self.assertEqual(version, "1.26.0") + + @stability_mode("") + def test_get_schema_url_for_signal_types_single_http_default(self): + url = _get_schema_url_for_signal_types( + [_OpenTelemetryStabilitySignalType.HTTP] + ) + self.assertEqual( + url, f"https://opentelemetry.io/schemas/{_LEGACY_SCHEMA_VERSION}" + ) + + @stability_mode("http") + def test_get_schema_url_for_signal_types_single_http_stable(self): + url = _get_schema_url_for_signal_types( + [_OpenTelemetryStabilitySignalType.HTTP] + ) + self.assertEqual(url, "https://opentelemetry.io/schemas/1.21.0") + + @stability_mode("database") + def test_get_schema_url_for_signal_types_single_database_stable(self): + url = _get_schema_url_for_signal_types( + [_OpenTelemetryStabilitySignalType.DATABASE] + ) + self.assertEqual(url, "https://opentelemetry.io/schemas/1.25.0") + + @stability_mode("http,database") + def test_get_schema_url_for_signal_types_multiple_both_stable(self): + # DATABASE has higher version (1.25.0) than HTTP (1.21.0) + url = _get_schema_url_for_signal_types( + [ + _OpenTelemetryStabilitySignalType.HTTP, + _OpenTelemetryStabilitySignalType.DATABASE, + ] + ) + self.assertEqual(url, "https://opentelemetry.io/schemas/1.25.0") + + @stability_mode("http") + def test_get_schema_url_for_signal_types_mixed_modes(self): + # HTTP is stable (1.21.0), DATABASE is default (1.11.0) + # Should return HTTP version as it's higher + url = _get_schema_url_for_signal_types( + [ + _OpenTelemetryStabilitySignalType.HTTP, + _OpenTelemetryStabilitySignalType.DATABASE, + ] + ) + self.assertEqual(url, "https://opentelemetry.io/schemas/1.21.0") + + @stability_mode("database") + def test_get_schema_url_for_signal_types_database_only_stable(self): + # DATABASE is stable (1.25.0), HTTP is default (1.11.0) + # Should return DATABASE version as it's highest + url = _get_schema_url_for_signal_types( + [ + _OpenTelemetryStabilitySignalType.HTTP, + _OpenTelemetryStabilitySignalType.DATABASE, + ] + ) + self.assertEqual(url, "https://opentelemetry.io/schemas/1.25.0") + + @stability_mode("") + def test_get_schema_url_for_signal_types_empty_list(self): + url = _get_schema_url_for_signal_types([]) + self.assertEqual( + url, f"https://opentelemetry.io/schemas/{_LEGACY_SCHEMA_VERSION}" + ) + + @stability_mode("http/dup,database/dup") + def test_get_schema_url_for_signal_types_dup_modes(self): + url = _get_schema_url_for_signal_types( + [ + _OpenTelemetryStabilitySignalType.HTTP, + _OpenTelemetryStabilitySignalType.DATABASE, + ] + ) + self.assertEqual(url, "https://opentelemetry.io/schemas/1.25.0") + + @stability_mode("http,database,gen_ai_latest_experimental") + def test_get_schema_url_for_signal_types_with_gen_ai(self): + # GEN_AI should be highest at 1.26.0 + url = _get_schema_url_for_signal_types( + [ + _OpenTelemetryStabilitySignalType.HTTP, + _OpenTelemetryStabilitySignalType.DATABASE, + _OpenTelemetryStabilitySignalType.GEN_AI, + ] + ) + self.assertEqual(url, "https://opentelemetry.io/schemas/1.26.0") + + class TestOpenTelemetrySemConvStabilityHTTP(TestCase): def test_set_status_for_non_http_code_with_recording_span(self): span = Mock() @@ -443,3 +577,45 @@ def test_db_user_none_value(self): result = {} _set_db_user(result, None, sem_conv_opt_in_mode=_StabilityMode.DEFAULT) self.assertNotIn(DB_USER, result) + + def test_db_operation_default(self): + result = {} + _set_db_operation( + result, + "SELECT", + sem_conv_opt_in_mode=_StabilityMode.DEFAULT, + ) + self.assertIn(DB_OPERATION, result) + self.assertEqual(result[DB_OPERATION], "SELECT") + self.assertNotIn(DB_OPERATION_NAME, result) + + def test_db_operation_database_stable(self): + result = {} + _set_db_operation( + result, + "SELECT", + sem_conv_opt_in_mode=_StabilityMode.DATABASE, + ) + self.assertNotIn(DB_OPERATION, result) + self.assertIn(DB_OPERATION_NAME, result) + self.assertEqual(result[DB_OPERATION_NAME], "SELECT") + + def test_db_operation_database_dup(self): + result = {} + _set_db_operation( + result, + "SELECT", + sem_conv_opt_in_mode=_StabilityMode.DATABASE_DUP, + ) + self.assertIn(DB_OPERATION, result) + self.assertEqual(result[DB_OPERATION], "SELECT") + self.assertIn(DB_OPERATION_NAME, result) + self.assertEqual(result[DB_OPERATION_NAME], "SELECT") + + def test_db_operation_none_value(self): + result = {} + _set_db_operation( + result, None, sem_conv_opt_in_mode=_StabilityMode.DEFAULT + ) + self.assertNotIn(DB_OPERATION, result) + self.assertNotIn(DB_OPERATION_NAME, result) diff --git a/opentelemetry-instrumentation/tests/test_utils.py b/opentelemetry-instrumentation/tests/test_utils.py index 5ddd45d692..04ccfc0cbc 100644 --- a/opentelemetry-instrumentation/tests/test_utils.py +++ b/opentelemetry-instrumentation/tests/test_utils.py @@ -12,10 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +import sys import unittest from http import HTTPStatus -from wrapt import ObjectProxy, wrap_function_wrapper +from wrapt import wrap_function_wrapper + +try: + # wrapt 2.0.0+ + from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module +except ImportError: + from wrapt import ObjectProxy as BaseObjectProxy from opentelemetry.context import ( _SUPPRESS_HTTP_INSTRUMENTATION_KEY, @@ -263,23 +270,23 @@ def _wrap_method(): def test_can_unwrap_object_attribute(self): self._wrap_method() instance = WrappedClass() - self.assertTrue(isinstance(instance.method, ObjectProxy)) + self.assertTrue(isinstance(instance.method, BaseObjectProxy)) unwrap(WrappedClass, "method") - self.assertFalse(isinstance(instance.method, ObjectProxy)) + self.assertFalse(isinstance(instance.method, BaseObjectProxy)) def test_can_unwrap_object_attribute_as_string(self): self._wrap_method() instance = WrappedClass() - self.assertTrue(isinstance(instance.method, ObjectProxy)) + self.assertTrue(isinstance(instance.method, BaseObjectProxy)) unwrap("tests.test_utils.WrappedClass", "method") - self.assertFalse(isinstance(instance.method, ObjectProxy)) + self.assertFalse(isinstance(instance.method, BaseObjectProxy)) def test_raises_import_error_if_path_not_well_formed(self): self._wrap_method() instance = WrappedClass() - self.assertTrue(isinstance(instance.method, ObjectProxy)) + self.assertTrue(isinstance(instance.method, BaseObjectProxy)) with self.assertRaisesRegex( ImportError, "Cannot parse '' as dotted import path" @@ -287,23 +294,31 @@ def test_raises_import_error_if_path_not_well_formed(self): unwrap("", "method") unwrap(WrappedClass, "method") - self.assertFalse(isinstance(instance.method, ObjectProxy)) + self.assertFalse(isinstance(instance.method, BaseObjectProxy)) + + def test_noop_if_module_not_imported(self): + # A module that exists but hasn't been imported, treated + # as no-op. + self.assertNotIn("email.generator", sys.modules) + unwrap("email.generator.BytesGenerator", "flatten") + self.assertNotIn("email.generator", sys.modules) - def test_raises_import_error_if_cannot_find_module(self): + def test_noop_if_cannot_find_module(self): self._wrap_method() instance = WrappedClass() - self.assertTrue(isinstance(instance.method, ObjectProxy)) + self.assertTrue(isinstance(instance.method, BaseObjectProxy)) - with self.assertRaisesRegex(ImportError, "No module named 'does'"): - unwrap("does.not.exist.WrappedClass", "method") + # Treated same as an existing module that hasn't been imported, + # as a no-op. + unwrap("does.not.exist.WrappedClass", "method") unwrap(WrappedClass, "method") - self.assertFalse(isinstance(instance.method, ObjectProxy)) + self.assertFalse(isinstance(instance.method, BaseObjectProxy)) def test_raises_import_error_if_cannot_find_object(self): self._wrap_method() instance = WrappedClass() - self.assertTrue(isinstance(instance.method, ObjectProxy)) + self.assertTrue(isinstance(instance.method, BaseObjectProxy)) with self.assertRaisesRegex( ImportError, "Cannot import 'NotWrappedClass' from" @@ -311,7 +326,7 @@ def test_raises_import_error_if_cannot_find_object(self): unwrap("tests.test_utils.NotWrappedClass", "method") unwrap(WrappedClass, "method") - self.assertFalse(isinstance(instance.method, ObjectProxy)) + self.assertFalse(isinstance(instance.method, BaseObjectProxy)) # pylint: disable=no-self-use def test_does_nothing_if_cannot_find_attribute(self): @@ -320,6 +335,6 @@ def test_does_nothing_if_cannot_find_attribute(self): def test_does_nothing_if_attribute_is_not_from_wrapt(self): instance = WrappedClass() - self.assertFalse(isinstance(instance.method, ObjectProxy)) + self.assertFalse(isinstance(instance.method, BaseObjectProxy)) unwrap(WrappedClass, "method") - self.assertFalse(isinstance(instance.method, ObjectProxy)) + self.assertFalse(isinstance(instance.method, BaseObjectProxy)) diff --git a/processor/opentelemetry-processor-baggage/pyproject.toml b/processor/opentelemetry-processor-baggage/pyproject.toml index 2c4098aeb6..f11bbd3609 100644 --- a/processor/opentelemetry-processor-baggage/pyproject.toml +++ b/processor/opentelemetry-processor-baggage/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry Baggage Span Processor" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -28,7 +27,7 @@ classifiers = [ dependencies = [ "opentelemetry-api ~= 1.5", "opentelemetry-sdk ~= 1.5", - "wrapt >= 1.0.0, < 2.0.0", + "wrapt >= 1.0.0, < 3.0.0", ] [project.urls] diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py index 7e09e591e0..d28cc1e68d 100644 --- a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py +++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/processor.py @@ -23,7 +23,7 @@ BaggageKeyPredicateT = Callable[[str], bool] # A BaggageKeyPredicate that always returns True, allowing all baggage keys to be added to spans -ALLOW_ALL_BAGGAGE_KEYS: BaggageKeyPredicateT = lambda _: True # noqa: E731 +ALLOW_ALL_BAGGAGE_KEYS: BaggageKeyPredicateT = lambda _: True # noqa: E731 # pylint:disable=invalid-name class BaggageSpanProcessor(SpanProcessor): diff --git a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py +++ b/processor/opentelemetry-processor-baggage/src/opentelemetry/processor/baggage/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/propagator/opentelemetry-propagator-aws-xray/pyproject.toml b/propagator/opentelemetry-propagator-aws-xray/pyproject.toml index 0f7240efa6..66f7d69550 100644 --- a/propagator/opentelemetry-propagator-aws-xray/pyproject.toml +++ b/propagator/opentelemetry-propagator-aws-xray/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "AWS X-Ray Propagator for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/propagator/opentelemetry-propagator-aws-xray/test-requirements-0.txt b/propagator/opentelemetry-propagator-aws-xray/test-requirements-0.txt index 9880271676..f33b41e46e 100644 --- a/propagator/opentelemetry-propagator-aws-xray/test-requirements-0.txt +++ b/propagator/opentelemetry-propagator-aws-xray/test-requirements-0.txt @@ -5,7 +5,7 @@ Deprecated==1.2.14 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytest-benchmark==4.0.0 diff --git a/propagator/opentelemetry-propagator-aws-xray/test-requirements-1.txt b/propagator/opentelemetry-propagator-aws-xray/test-requirements-1.txt index 679800462e..b3daf5cfca 100644 --- a/propagator/opentelemetry-propagator-aws-xray/test-requirements-1.txt +++ b/propagator/opentelemetry-propagator-aws-xray/test-requirements-1.txt @@ -5,7 +5,7 @@ Deprecated==1.2.14 idna==3.7 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytest-benchmark==4.0.0 diff --git a/propagator/opentelemetry-propagator-ot-trace/pyproject.toml b/propagator/opentelemetry-propagator-ot-trace/pyproject.toml index 74f1503331..3569d4842a 100644 --- a/propagator/opentelemetry-propagator-ot-trace/pyproject.toml +++ b/propagator/opentelemetry-propagator-ot-trace/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OT Trace Propagator for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/version.py b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/version.py +++ b/propagator/opentelemetry-propagator-ot-trace/src/opentelemetry/propagators/ot_trace/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt b/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt index 379c534e26..97b8f51755 100644 --- a/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt +++ b/propagator/opentelemetry-propagator-ot-trace/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/pyproject.toml b/pyproject.toml index 99ba8d2e32..92cfe8b5d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,15 +2,16 @@ [project] name = "opentelemetry-python-contrib" version = "0.0.0" # This is not used. -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "opentelemetry-api", "opentelemetry-sdk", "opentelemetry-semantic-conventions", + "opentelemetry-instrumentation", + "opentelemetry-distro", "opentelemetry-test-utils", "opentelemetry-exporter-prometheus-remote-write", "opentelemetry-exporter-richconsole", - "opentelemetry-instrumentation", "opentelemetry-instrumentation-aio-pika[instruments]", "opentelemetry-instrumentation-aiohttp-client[instruments]", "opentelemetry-instrumentation-aiohttp-server[instruments]", @@ -20,9 +21,8 @@ dependencies = [ "opentelemetry-instrumentation-asyncio", "opentelemetry-instrumentation-asyncpg[instruments]", "opentelemetry-instrumentation-aws-lambda[instruments]", - "opentelemetry-instrumentation-boto[instruments]", "opentelemetry-instrumentation-boto3sqs[instruments]", - "opentelemetry-instrumentation-botocore[instruments]", + "opentelemetry-instrumentation-botocore[instruments-any]", "opentelemetry-instrumentation-cassandra[instruments]", "opentelemetry-instrumentation-celery[instruments]", "opentelemetry-instrumentation-click[instruments]", @@ -81,9 +81,11 @@ opentelemetry-api = { git = "https://github.com/open-telemetry/opentelemetry-pyt opentelemetry-sdk = { git = "https://github.com/open-telemetry/opentelemetry-python", branch = "main", subdirectory = "opentelemetry-sdk" } opentelemetry-semantic-conventions = { git = "https://github.com/open-telemetry/opentelemetry-python", branch = "main", subdirectory = "opentelemetry-semantic-conventions" } opentelemetry-test-utils = { git = "https://github.com/open-telemetry/opentelemetry-python", branch = "main", subdirectory = "tests/opentelemetry-test-utils" } +opentelemetry-exporter-otlp = { git = "https://github.com/open-telemetry/opentelemetry-python", branch = "main", subdirectory = "exporter/opentelemetry-exporter-otlp" } +opentelemetry-instrumentation = { workspace = true } +opentelemetry-distro = { workspace = true } opentelemetry-exporter-prometheus-remote-write = { workspace = true } opentelemetry-exporter-richconsole = { workspace = true } -opentelemetry-instrumentation = { workspace = true } opentelemetry-instrumentation-aio-pika = { workspace = true } opentelemetry-instrumentation-aiohttp-client = { workspace = true } opentelemetry-instrumentation-aiohttp-server = { workspace = true } @@ -93,7 +95,6 @@ opentelemetry-instrumentation-asgi = { workspace = true } opentelemetry-instrumentation-asyncio = { workspace = true } opentelemetry-instrumentation-asyncpg = { workspace = true } opentelemetry-instrumentation-aws-lambda = { workspace = true } -opentelemetry-instrumentation-boto = { workspace = true } opentelemetry-instrumentation-boto3sqs = { workspace = true } opentelemetry-instrumentation-botocore = { workspace = true } opentelemetry-instrumentation-cassandra = { workspace = true } @@ -146,13 +147,14 @@ members = [ "instrumentation-genai/*", "exporter/*", "opentelemetry-instrumentation", + "opentelemetry-distro", "propagator/*", "util/*", ] [tool.ruff] # https://docs.astral.sh/ruff/configuration/ -target-version = "py39" +target-version = "py310" line-length = 79 extend-exclude = ["_template", "*_pb2*.py*"] output-format = "concise" @@ -194,7 +196,7 @@ known-third-party = [ typeCheckingMode = "strict" reportUnnecessaryTypeIgnoreComment = true reportMissingTypeStubs = false -pythonVersion = "3.9" +pythonVersion = "3.10" reportPrivateUsage = false # Ignore private attributes added by instrumentation packages. # Add progressively instrumentation packages here. include = [ @@ -209,6 +211,7 @@ include = [ "util/opentelemetry-util-genai", "exporter/opentelemetry-exporter-credential-provider-gcp", "instrumentation/opentelemetry-instrumentation-aiohttp-client", + "opamp/opentelemetry-opamp-client", ] # We should also add type hints to the test suite - It helps on finding bugs. # We are excluding for now because it's easier, and more important to add to the instrumentation packages. @@ -228,6 +231,8 @@ exclude = [ "instrumentation-genai/opentelemetry-instrumentation-weaviate/examples/**/*.py", "util/opentelemetry-util-genai/tests/**/*.py", "instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/**/*.py", + "opamp/opentelemetry-opamp-client/tests/**/*.py", + "opamp/opentelemetry-opamp-client/src/opentelemetry/**/proto/**", ] [dependency-groups] diff --git a/resource/opentelemetry-resource-detector-azure/pyproject.toml b/resource/opentelemetry-resource-detector-azure/pyproject.toml index b6c27d7132..99c5cebb96 100644 --- a/resource/opentelemetry-resource-detector-azure/pyproject.toml +++ b/resource/opentelemetry-resource-detector-azure/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Azure Resource Detector for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/resource/opentelemetry-resource-detector-azure/test-requirements-0.txt b/resource/opentelemetry-resource-detector-azure/test-requirements-0.txt index e694db3b52..792cae9465 100644 --- a/resource/opentelemetry-resource-detector-azure/test-requirements-0.txt +++ b/resource/opentelemetry-resource-detector-azure/test-requirements-0.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 typing_extensions==4.12.2 diff --git a/resource/opentelemetry-resource-detector-azure/test-requirements-1.txt b/resource/opentelemetry-resource-detector-azure/test-requirements-1.txt index a2344e2871..5d78dff62b 100644 --- a/resource/opentelemetry-resource-detector-azure/test-requirements-1.txt +++ b/resource/opentelemetry-resource-detector-azure/test-requirements-1.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 typing_extensions==4.12.2 diff --git a/resource/opentelemetry-resource-detector-containerid/pyproject.toml b/resource/opentelemetry-resource-detector-containerid/pyproject.toml index 46875adb09..de1ec19717 100644 --- a/resource/opentelemetry-resource-detector-containerid/pyproject.toml +++ b/resource/opentelemetry-resource-detector-containerid/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Container Resource Detector for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/resource/opentelemetry-resource-detector-containerid/src/opentelemetry/resource/detector/containerid/version.py b/resource/opentelemetry-resource-detector-containerid/src/opentelemetry/resource/detector/containerid/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/resource/opentelemetry-resource-detector-containerid/src/opentelemetry/resource/detector/containerid/version.py +++ b/resource/opentelemetry-resource-detector-containerid/src/opentelemetry/resource/detector/containerid/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/resource/opentelemetry-resource-detector-containerid/test-requirements.txt b/resource/opentelemetry-resource-detector-containerid/test-requirements.txt index fbad04cd33..db0cc5ab58 100644 --- a/resource/opentelemetry-resource-detector-containerid/test-requirements.txt +++ b/resource/opentelemetry-resource-detector-containerid/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/scripts/build.sh b/scripts/build.sh index c652e399b6..fd5a207cae 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -16,7 +16,7 @@ DISTDIR=dist mkdir -p $DISTDIR rm -rf ${DISTDIR:?}/* - for d in exporter/*/ opentelemetry-instrumentation/ opentelemetry-contrib-instrumentations/ opentelemetry-distro/ instrumentation/*/ processor/*/ propagator/*/ resource/*/ sdk-extension/*/ util/*/ ; do + for d in exporter/*/ opentelemetry-instrumentation/ opentelemetry-contrib-instrumentations/ opentelemetry-distro/ instrumentation/*/ processor/*/ propagator/*/ resource/*/ sdk-extension/*/ util/*/ opamp/*/ ; do ( echo "building $d" cd "$d" diff --git a/scripts/opamp_proto_codegen.sh b/scripts/opamp_proto_codegen.sh new file mode 100755 index 0000000000..23db6e354f --- /dev/null +++ b/scripts/opamp_proto_codegen.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Regenerate python code from opamp protos in +# https://github.com/open-telemetry/opamp-spec +# +# To use, update OPAMP_SPEC_REPO_BRANCH_OR_COMMIT variable below to a commit hash or +# tag in opentelemtry-proto repo that you want to build off of. Then, just run +# this script to update the proto files. Commit the changes as well as any +# fixes needed in the OTLP exporter. +# +# Optional envars: +# OPAMP_SPEC_REPO_DIR - the path to an existing checkout of the opamp-spec repo + +# Pinned commit/branch/tag for the current version used in the opamp python package. +OPAMP_SPEC_REPO_BRANCH_OR_COMMIT="v0.12.0" + +set -e + +OPAMP_SPEC_REPO_DIR=${OPAMP_SPEC_REPO_DIR:-"/tmp/opamp-spec"} +# root of opentelemetry-python repo +repo_root="$(git rev-parse --show-toplevel)" +proto_output_dir="$repo_root/opamp/opentelemetry-opamp-client/src/opentelemetry/_opamp/proto" + +protoc() { + uvx -c $repo_root/opamp-gen-requirements.txt \ + --python 3.12 \ + --from grpcio-tools \ + --with mypy-protobuf \ + python -m grpc_tools.protoc "$@" +} + +protoc --version + +# Clone the proto repo if it doesn't exist +if [ ! -d "$OPAMP_SPEC_REPO_DIR" ]; then + git clone https://github.com/open-telemetry/opamp-spec.git $OPAMP_SPEC_REPO_DIR +fi + +# Pull in changes and switch to requested branch +( + cd $OPAMP_SPEC_REPO_DIR + git fetch --all + git checkout $OPAMP_SPEC_REPO_BRANCH_OR_COMMIT + # pull if OPAMP_SPEC_BRANCH_OR_COMMIT is not a detached head + git symbolic-ref -q HEAD && git pull --ff-only || true +) + +cd $proto_output_dir + +# clean up old generated code +find . -regex ".*_pb2.*\.pyi?" -exec rm {} + + +# generate proto code for all protos +all_protos=$(find $OPAMP_SPEC_REPO_DIR/ -name "*.proto") +protoc \ + -I $OPAMP_SPEC_REPO_DIR/proto \ + --python_out=. \ + --mypy_out=. \ + $all_protos + +sed -i -e 's/import anyvalue_pb2 as anyvalue__pb2/from . import anyvalue_pb2 as anyvalue__pb2/' opamp_pb2.py diff --git a/scripts/update_sha.py b/scripts/update_sha.py deleted file mode 100644 index c63b9eda9b..0000000000 --- a/scripts/update_sha.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint: disable=import-error,unspecified-encoding - -import argparse - -import requests -from ruamel.yaml import YAML - -API_URL = ( - "https://api.github.com/repos/open-telemetry/opentelemetry-python/commits/" -) -workflow_files = [ - ".github/workflows/test_0.yml" - ".github/workflows/test_1.yml" - ".github/workflows/misc_0.yml" - ".github/workflows/lint_0.yml" -] - - -def get_sha(branch): - url = API_URL + branch - response = requests.get(url, timeout=15) - response.raise_for_status() - return response.json()["sha"] - - -def update_sha(sha): - yaml = YAML() - yaml.preserve_quotes = True - for workflow_file in workflow_files: - with open(workflow_file, "r") as file: - workflow = yaml.load(file) - workflow["env"]["CORE_REPO_SHA"] = sha - with open(workflow_file, "w") as file: - yaml.dump(workflow, file) - - -def main(): - args = parse_args() - sha = get_sha(args.branch) - update_sha(sha) - - -def parse_args(): - parser = argparse.ArgumentParser( - description="Updates the SHA in the workflow file" - ) - parser.add_argument("-b", "--branch", help="branch to use") - return parser.parse_args() - - -if __name__ == "__main__": - main() diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml b/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml index 10e18b9ef1..716c3f1204 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml +++ b/sdk-extension/opentelemetry-sdk-extension-aws/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "AWS SDK extension for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/sampler/_sampling_rule.py b/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/sampler/_sampling_rule.py index fd99b2a87f..0ee531017e 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/sampler/_sampling_rule.py +++ b/sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/trace/sampler/_sampling_rule.py @@ -20,7 +20,7 @@ # Disable snake_case naming style so this class can match the sampling rules response from X-Ray -# pylint: disable=invalid-name +# pylint: disable=invalid-name,too-many-positional-arguments class _SamplingRule: def __init__( self, diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-0.txt b/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-0.txt index e5986b6ada..53757d6364 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-0.txt +++ b/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-0.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytest-benchmark==4.0.0 diff --git a/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-1.txt b/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-1.txt index 997a373383..b702824d72 100644 --- a/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-1.txt +++ b/sdk-extension/opentelemetry-sdk-extension-aws/test-requirements-1.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 pytest-benchmark==4.0.0 diff --git a/tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py b/tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py index e14ba07271..6a2499fdb4 100644 --- a/tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py +++ b/tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py @@ -12,7 +12,17 @@ from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, ) -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_STATEMENT, + DB_SYSTEM, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, + NET_TRANSPORT, +) from opentelemetry.test.test_base import TestBase from opentelemetry.trace import StatusCode @@ -30,21 +40,11 @@ def async_call(coro): class CheckSpanMixin: def check_span(self, span, expected_db_name=POSTGRES_DB_NAME): - self.assertEqual( - span.attributes[SpanAttributes.DB_SYSTEM], "postgresql" - ) - self.assertEqual( - span.attributes[SpanAttributes.DB_NAME], expected_db_name - ) - self.assertEqual( - span.attributes[SpanAttributes.DB_USER], POSTGRES_USER - ) - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_NAME], POSTGRES_HOST - ) - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_PORT], POSTGRES_PORT - ) + self.assertEqual(span.attributes[DB_SYSTEM], "postgresql") + self.assertEqual(span.attributes[DB_NAME], expected_db_name) + self.assertEqual(span.attributes[DB_USER], POSTGRES_USER) + self.assertEqual(span.attributes[NET_PEER_NAME], POSTGRES_HOST) + self.assertEqual(span.attributes[NET_PEER_PORT], POSTGRES_PORT) class TestFunctionalAsyncPG(TestBase, CheckSpanMixin): @@ -74,9 +74,7 @@ def test_instrumented_execute_method_without_arguments(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, "SELECT") - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT 42;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT 42;") def test_instrumented_execute_method_error(self, *_, **__): """Should create an error span for execute() with the database name as the span name.""" @@ -87,7 +85,7 @@ def test_instrumented_execute_method_error(self, *_, **__): self.assertIs(StatusCode.ERROR, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, POSTGRES_DB_NAME) - self.assertEqual(spans[0].attributes[SpanAttributes.DB_STATEMENT], "") + self.assertEqual(spans[0].attributes[DB_STATEMENT], "") def test_instrumented_fetch_method_without_arguments(self, *_, **__): """Should create a span from fetch().""" @@ -97,9 +95,7 @@ def test_instrumented_fetch_method_without_arguments(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, "SELECT") - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT 42;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT 42;") def test_instrumented_fetch_method_empty_query(self, *_, **__): """Should create an error span for fetch() with the database name as the span name.""" @@ -109,7 +105,7 @@ def test_instrumented_fetch_method_empty_query(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, POSTGRES_DB_NAME) - self.assertEqual(spans[0].attributes[SpanAttributes.DB_STATEMENT], "") + self.assertEqual(spans[0].attributes[DB_STATEMENT], "") def test_instrumented_fetchval_method_without_arguments(self, *_, **__): """Should create a span for fetchval().""" @@ -119,9 +115,7 @@ def test_instrumented_fetchval_method_without_arguments(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, "SELECT") - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT 42;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT 42;") def test_instrumented_fetchval_method_empty_query(self, *_, **__): """Should create an error span for fetchval() with the database name as the span name.""" @@ -131,7 +125,7 @@ def test_instrumented_fetchval_method_empty_query(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, POSTGRES_DB_NAME) - self.assertEqual(spans[0].attributes[SpanAttributes.DB_STATEMENT], "") + self.assertEqual(spans[0].attributes[DB_STATEMENT], "") def test_instrumented_fetchrow_method_without_arguments(self, *_, **__): """Should create a span for fetchrow().""" @@ -141,9 +135,7 @@ def test_instrumented_fetchrow_method_without_arguments(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, "SELECT") - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT 42;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT 42;") def test_instrumented_fetchrow_method_empty_query(self, *_, **__): """Should create an error span for fetchrow() with the database name as the span name.""" @@ -153,7 +145,7 @@ def test_instrumented_fetchrow_method_empty_query(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, POSTGRES_DB_NAME) - self.assertEqual(spans[0].attributes[SpanAttributes.DB_STATEMENT], "") + self.assertEqual(spans[0].attributes[DB_STATEMENT], "") def test_instrumented_cursor_execute_method_without_arguments( self, *_, **__ @@ -172,25 +164,21 @@ async def _cursor_execute(): self.check_span(spans[0]) self.assertEqual(spans[0].name, "BEGIN;") - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "BEGIN;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "BEGIN;") self.assertIs(StatusCode.UNSET, spans[0].status.status_code) for span in spans[1:-1]: self.check_span(span) self.assertEqual(span.name, "CURSOR: SELECT") self.assertEqual( - span.attributes[SpanAttributes.DB_STATEMENT], + span.attributes[DB_STATEMENT], "SELECT generate_series(0, 5);", ) self.assertIs(StatusCode.UNSET, span.status.status_code) self.check_span(spans[-1]) self.assertEqual(spans[-1].name, "COMMIT;") - self.assertEqual( - spans[-1].attributes[SpanAttributes.DB_STATEMENT], "COMMIT;" - ) + self.assertEqual(spans[-1].attributes[DB_STATEMENT], "COMMIT;") def test_instrumented_cursor_execute_method_empty_query(self, *_, **__): """Should create spans for the transaction and cursor fetches with the database name as the span name.""" @@ -205,20 +193,16 @@ async def _cursor_execute(): self.assertEqual(len(spans), 3) self.check_span(spans[0]) - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "BEGIN;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "BEGIN;") self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[1]) self.assertEqual(spans[1].name, f"CURSOR: {POSTGRES_DB_NAME}") - self.assertEqual(spans[1].attributes[SpanAttributes.DB_STATEMENT], "") + self.assertEqual(spans[1].attributes[DB_STATEMENT], "") self.assertIs(StatusCode.UNSET, spans[1].status.status_code) self.check_span(spans[2]) - self.assertEqual( - spans[2].attributes[SpanAttributes.DB_STATEMENT], "COMMIT;" - ) + self.assertEqual(spans[2].attributes[DB_STATEMENT], "COMMIT;") def test_instrumented_remove_comments(self, *_, **__): """Should remove comments from the query and set the span name correctly.""" @@ -235,21 +219,21 @@ def test_instrumented_remove_comments(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.assertEqual(spans[0].name, "SELECT") self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], + spans[0].attributes[DB_STATEMENT], "/* leading comment */ SELECT 42;", ) self.check_span(spans[1]) self.assertIs(StatusCode.UNSET, spans[1].status.status_code) self.assertEqual(spans[1].name, "SELECT") self.assertEqual( - spans[1].attributes[SpanAttributes.DB_STATEMENT], + spans[1].attributes[DB_STATEMENT], "/* leading comment */ SELECT 42; /* trailing comment */", ) self.check_span(spans[2]) self.assertIs(StatusCode.UNSET, spans[2].status.status_code) self.assertEqual(spans[2].name, "SELECT") self.assertEqual( - spans[2].attributes[SpanAttributes.DB_STATEMENT], + spans[2].attributes[DB_STATEMENT], "SELECT 42; /* trailing comment */", ) @@ -265,21 +249,15 @@ async def _transaction_execute(): spans = self.memory_exporter.get_finished_spans() self.assertEqual(3, len(spans)) self.check_span(spans[0]) - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "BEGIN;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "BEGIN;") self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[1]) - self.assertEqual( - spans[1].attributes[SpanAttributes.DB_STATEMENT], "SELECT 42;" - ) + self.assertEqual(spans[1].attributes[DB_STATEMENT], "SELECT 42;") self.assertIs(StatusCode.UNSET, spans[1].status.status_code) self.check_span(spans[2]) - self.assertEqual( - spans[2].attributes[SpanAttributes.DB_STATEMENT], "COMMIT;" - ) + self.assertEqual(spans[2].attributes[DB_STATEMENT], "COMMIT;") self.assertIs(StatusCode.UNSET, spans[2].status.status_code) def test_instrumented_failed_transaction_method(self, *_, **__): @@ -296,22 +274,18 @@ async def _transaction_execute(): self.assertEqual(3, len(spans)) self.check_span(spans[0]) - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "BEGIN;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "BEGIN;") self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[1]) self.assertEqual( - spans[1].attributes[SpanAttributes.DB_STATEMENT], + spans[1].attributes[DB_STATEMENT], "SELECT 42::uuid;", ) self.assertEqual(StatusCode.ERROR, spans[1].status.status_code) self.check_span(spans[2]) - self.assertEqual( - spans[2].attributes[SpanAttributes.DB_STATEMENT], "ROLLBACK;" - ) + self.assertEqual(spans[2].attributes[DB_STATEMENT], "ROLLBACK;") self.assertIs(StatusCode.UNSET, spans[2].status.status_code) def test_instrumented_method_doesnt_capture_parameters(self, *_, **__): @@ -321,9 +295,7 @@ def test_instrumented_method_doesnt_capture_parameters(self, *_, **__): self.assertEqual(len(spans), 1) self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT $1;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT $1;") class TestFunctionalAsyncPG_CaptureParameters(TestBase, CheckSpanMixin): @@ -356,9 +328,7 @@ def test_instrumented_execute_method_with_arguments(self, *_, **__): self.check_span(spans[0]) self.assertEqual(spans[0].name, "SELECT") - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT $1;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT $1;") self.assertEqual( spans[0].attributes["db.statement.parameters"], "('1',)" ) @@ -371,9 +341,7 @@ def test_instrumented_fetch_method_with_arguments(self, *_, **__): self.check_span(spans[0]) self.assertEqual(spans[0].name, "SELECT") - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT $1;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT $1;") self.assertEqual( spans[0].attributes["db.statement.parameters"], "('1',)" ) @@ -385,9 +353,7 @@ def test_instrumented_executemany_method_with_arguments(self, *_, **__): self.assertEqual(len(spans), 1) self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT $1;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT $1;") self.assertEqual( spans[0].attributes["db.statement.parameters"], "([['1'], ['2']],)" ) @@ -400,9 +366,7 @@ def test_instrumented_execute_interface_error_method(self, *_, **__): self.assertEqual(len(spans), 1) self.assertIs(StatusCode.ERROR, spans[0].status.status_code) self.check_span(spans[0]) - self.assertEqual( - spans[0].attributes[SpanAttributes.DB_STATEMENT], "SELECT 42;" - ) + self.assertEqual(spans[0].attributes[DB_STATEMENT], "SELECT 42;") self.assertEqual( spans[0].attributes["db.statement.parameters"], "(1, 2, 3)" ) @@ -415,7 +379,7 @@ def test_instrumented_executemany_method_empty_query(self, *_, **__): self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.check_span(spans[0]) self.assertEqual(spans[0].name, POSTGRES_DB_NAME) - self.assertEqual(spans[0].attributes[SpanAttributes.DB_STATEMENT], "") + self.assertEqual(spans[0].attributes[DB_STATEMENT], "") self.assertEqual( spans[0].attributes["db.statement.parameters"], "([],)" ) @@ -430,7 +394,7 @@ def test_instrumented_fetch_method_broken_asyncpg(self, *_, **__): self.assertEqual(len(spans), 1) self.assertIs(StatusCode.UNSET, spans[0].status.status_code) self.assertEqual(spans[0].name, "postgresql") - self.assertEqual(spans[0].attributes[SpanAttributes.DB_STATEMENT], "") + self.assertEqual(spans[0].attributes[DB_STATEMENT], "") class _FakeParams: @@ -482,13 +446,13 @@ async def _fake_execute(*args, **kwargs): self.assertEqual( span.attributes, { - SpanAttributes.DB_SYSTEM: "postgresql", - SpanAttributes.DB_NAME: "testdb", - SpanAttributes.DB_USER: "dbuser", - SpanAttributes.NET_PEER_NAME: "db.example.com", - SpanAttributes.NET_PEER_PORT: 5432, - SpanAttributes.NET_TRANSPORT: "ip_tcp", - SpanAttributes.DB_STATEMENT: "SELECT $1", + DB_SYSTEM: "postgresql", + DB_NAME: "testdb", + DB_USER: "dbuser", + NET_PEER_NAME: "db.example.com", + NET_PEER_PORT: 5432, + NET_TRANSPORT: "ip_tcp", + DB_STATEMENT: "SELECT $1", "db.statement.parameters": "('42',)", }, ) @@ -516,13 +480,13 @@ async def _fake_cursor_execute(*args, **kwargs): self.assertEqual( span.attributes, { - SpanAttributes.DB_SYSTEM: "postgresql", - SpanAttributes.DB_NAME: "testdb", - SpanAttributes.DB_USER: "dbuser", - SpanAttributes.NET_PEER_NAME: "db.example.com", - SpanAttributes.NET_PEER_PORT: 5432, - SpanAttributes.NET_TRANSPORT: "ip_tcp", - SpanAttributes.DB_STATEMENT: "SELECT $1", + DB_SYSTEM: "postgresql", + DB_NAME: "testdb", + DB_USER: "dbuser", + NET_PEER_NAME: "db.example.com", + NET_PEER_PORT: 5432, + NET_TRANSPORT: "ip_tcp", + DB_STATEMENT: "SELECT $1", "db.statement.parameters": "('99',)", }, ) diff --git a/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py b/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py index 0b9cb3dac5..b1b17204ec 100644 --- a/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py +++ b/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py @@ -22,7 +22,13 @@ from opentelemetry.instrumentation.celery import CeleryInstrumentor from opentelemetry.sdk import resources from opentelemetry.sdk.trace import TracerProvider, export -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.exception_attributes import ( + EXCEPTION_MESSAGE, + EXCEPTION_TYPE, +) +from opentelemetry.semconv._incubating.attributes.messaging_attributes import ( + MESSAGING_MESSAGE_ID, +) from opentelemetry.trace import StatusCode # set a high timeout for async executions due to issues in CI @@ -105,9 +111,7 @@ def fn_task(): assert span.status.is_ok is True assert span.name == "run/test_celery_functional.fn_task" - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) == t.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == t.task_id assert ( span.attributes.get("celery.task_name") == "test_celery_functional.fn_task" @@ -132,9 +136,7 @@ def fn_task(self): assert span.status.is_ok is True assert span.name == "run/test_celery_functional.fn_task" - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) == t.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == t.task_id assert ( span.attributes.get("celery.task_name") == "test_celery_functional.fn_task" @@ -167,10 +169,7 @@ def fn_task_parameters(user, force_logout=False): == "apply_async/test_celery_functional.fn_task_parameters" ) assert async_span.attributes.get("celery.action") == "apply_async" - assert ( - async_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert async_span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id assert ( async_span.attributes.get("celery.task_name") == "test_celery_functional.fn_task_parameters" @@ -180,10 +179,7 @@ def fn_task_parameters(user, force_logout=False): assert run_span.name == "test_celery_functional.fn_task_parameters" assert run_span.attributes.get("celery.action") == "run" assert run_span.attributes.get("celery.state") == "SUCCESS" - assert ( - run_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert run_span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id assert ( run_span.attributes.get("celery.task_name") == "test_celery_functional.fn_task_parameters" @@ -228,10 +224,7 @@ def fn_task_parameters(user, force_logout=False): == "apply_async/test_celery_functional.fn_task_parameters" ) assert async_span.attributes.get("celery.action") == "apply_async" - assert ( - async_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert async_span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id assert ( async_span.attributes.get("celery.task_name") == "test_celery_functional.fn_task_parameters" @@ -241,10 +234,7 @@ def fn_task_parameters(user, force_logout=False): assert run_span.name == "run/test_celery_functional.fn_task_parameters" assert run_span.attributes.get("celery.action") == "run" assert run_span.attributes.get("celery.state") == "SUCCESS" - assert ( - run_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert run_span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id assert ( run_span.attributes.get("celery.task_name") == "test_celery_functional.fn_task_parameters" @@ -279,12 +269,9 @@ def fn_exception(): assert len(span.events) == 1 event = span.events[0] assert event.name == "exception" - assert event.attributes[SpanAttributes.EXCEPTION_TYPE] == "Exception" - assert SpanAttributes.EXCEPTION_MESSAGE in event.attributes - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert event.attributes[EXCEPTION_TYPE] == "Exception" + assert EXCEPTION_MESSAGE in event.attributes + assert span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id assert "Task class is failing" in span.status.description @@ -312,10 +299,7 @@ def fn_exception(): span.attributes.get("celery.task_name") == "test_celery_functional.fn_exception" ) - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id def test_fn_retry_exception(celery_app, memory_exporter): @@ -342,10 +326,7 @@ def fn_exception(): span.attributes.get("celery.task_name") == "test_celery_functional.fn_exception" ) - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id def test_class_task(celery_app, memory_exporter): @@ -377,10 +358,7 @@ def run(self): ) assert span.attributes.get("celery.action") == "run" assert span.attributes.get("celery.state") == "SUCCESS" - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id def test_class_task_exception(celery_app, memory_exporter): @@ -413,10 +391,7 @@ def run(self): assert span.attributes.get("celery.action") == "run" assert span.attributes.get("celery.state") == "FAILURE" assert span.status.status_code == StatusCode.ERROR - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id assert "Task class is failing" in span.status.description @@ -448,10 +423,7 @@ def run(self): assert span.name == "run/test_celery_functional.BaseTask" assert span.attributes.get("celery.action") == "run" assert span.attributes.get("celery.state") == "FAILURE" - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id def test_shared_task(celery_app, memory_exporter): @@ -476,10 +448,7 @@ def add(x, y): ) assert span.attributes.get("celery.action") == "run" assert span.attributes.get("celery.state") == "SUCCESS" - assert ( - span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id @mark.skip(reason="inconsistent test results") @@ -526,10 +495,7 @@ class CelerySubClass(CelerySuperClass): ) assert run_span.attributes.get("celery.action") == "run" assert run_span.attributes.get("celery.state") == "SUCCESS" - assert ( - run_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - == result.task_id - ) + assert run_span.attributes.get(MESSAGING_MESSAGE_ID) == result.task_id assert async_run_span.status.is_ok is True assert async_run_span.name == "run/test_celery_functional.CelerySubClass" @@ -540,8 +506,7 @@ class CelerySubClass(CelerySuperClass): assert async_run_span.attributes.get("celery.action") == "run" assert async_run_span.attributes.get("celery.state") == "SUCCESS" assert ( - async_run_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - != result.task_id + async_run_span.attributes.get(MESSAGING_MESSAGE_ID) != result.task_id ) assert async_span.status.is_ok is True @@ -553,13 +518,10 @@ class CelerySubClass(CelerySuperClass): == "test_celery_functional.CelerySubClass" ) assert async_span.attributes.get("celery.action") == "apply_async" - assert ( - async_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) - != result.task_id - ) + assert async_span.attributes.get(MESSAGING_MESSAGE_ID) != result.task_id assert async_span.attributes.get( - SpanAttributes.MESSAGING_MESSAGE_ID - ) == async_run_span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID) + MESSAGING_MESSAGE_ID + ) == async_run_span.attributes.get(MESSAGING_MESSAGE_ID) def test_custom_tracer_provider(celery_app, memory_exporter): diff --git a/tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py b/tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py index 7f5b94fc5d..30bcc6a298 100644 --- a/tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py +++ b/tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py @@ -18,7 +18,15 @@ from opentelemetry import trace as trace_api from opentelemetry.instrumentation.mysql import MySQLInstrumentor -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_SYSTEM, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from opentelemetry.test.test_base import TestBase MYSQL_USER = os.getenv("MYSQL_USER", "testuser") @@ -65,19 +73,11 @@ def validate_spans(self, span_name): self.assertIsNotNone(db_span.parent) self.assertIs(db_span.parent, root_span.get_span_context()) self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual(db_span.attributes[SpanAttributes.DB_SYSTEM], "mysql") - self.assertEqual( - db_span.attributes[SpanAttributes.DB_NAME], MYSQL_DB_NAME - ) - self.assertEqual( - db_span.attributes[SpanAttributes.DB_USER], MYSQL_USER - ) - self.assertEqual( - db_span.attributes[SpanAttributes.NET_PEER_NAME], MYSQL_HOST - ) - self.assertEqual( - db_span.attributes[SpanAttributes.NET_PEER_PORT], MYSQL_PORT - ) + self.assertEqual(db_span.attributes[DB_SYSTEM], "mysql") + self.assertEqual(db_span.attributes[DB_NAME], MYSQL_DB_NAME) + self.assertEqual(db_span.attributes[DB_USER], MYSQL_USER) + self.assertEqual(db_span.attributes[NET_PEER_NAME], MYSQL_HOST) + self.assertEqual(db_span.attributes[NET_PEER_PORT], MYSQL_PORT) def test_execute(self): """Should create a child span for execute""" diff --git a/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py b/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py index 93ac71a5db..437a4332f1 100644 --- a/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py +++ b/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py @@ -20,7 +20,15 @@ from opentelemetry import trace as trace_api from opentelemetry.instrumentation.aiopg import AiopgInstrumentor -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_SYSTEM, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from opentelemetry.test.test_base import TestBase POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "127.0.0.1") @@ -74,21 +82,11 @@ def validate_spans(self, span_name): self.assertIsNotNone(child_span.parent) self.assertIs(child_span.parent, root_span.get_span_context()) self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_SYSTEM], "postgresql" - ) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_NAME], POSTGRES_DB_NAME - ) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_USER], POSTGRES_USER - ) - self.assertEqual( - child_span.attributes[SpanAttributes.NET_PEER_NAME], POSTGRES_HOST - ) - self.assertEqual( - child_span.attributes[SpanAttributes.NET_PEER_PORT], POSTGRES_PORT - ) + self.assertEqual(child_span.attributes[DB_SYSTEM], "postgresql") + self.assertEqual(child_span.attributes[DB_NAME], POSTGRES_DB_NAME) + self.assertEqual(child_span.attributes[DB_USER], POSTGRES_USER) + self.assertEqual(child_span.attributes[NET_PEER_NAME], POSTGRES_HOST) + self.assertEqual(child_span.attributes[NET_PEER_PORT], POSTGRES_PORT) def test_execute(self): """Should create a child span for execute method""" @@ -161,21 +159,11 @@ def validate_spans(self, span_name): self.assertIsNotNone(child_span.parent) self.assertIs(child_span.parent, root_span.get_span_context()) self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_SYSTEM], "postgresql" - ) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_NAME], POSTGRES_DB_NAME - ) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_USER], POSTGRES_USER - ) - self.assertEqual( - child_span.attributes[SpanAttributes.NET_PEER_NAME], POSTGRES_HOST - ) - self.assertEqual( - child_span.attributes[SpanAttributes.NET_PEER_PORT], POSTGRES_PORT - ) + self.assertEqual(child_span.attributes[DB_SYSTEM], "postgresql") + self.assertEqual(child_span.attributes[DB_NAME], POSTGRES_DB_NAME) + self.assertEqual(child_span.attributes[DB_USER], POSTGRES_USER) + self.assertEqual(child_span.attributes[NET_PEER_NAME], POSTGRES_HOST) + self.assertEqual(child_span.attributes[NET_PEER_PORT], POSTGRES_PORT) def test_execute(self): """Should create a child span for execute method""" diff --git a/tests/opentelemetry-docker-tests/tests/postgres/test_psycopg_functional.py b/tests/opentelemetry-docker-tests/tests/postgres/test_psycopg_functional.py index 4e2106ef8d..6cc800dd04 100644 --- a/tests/opentelemetry-docker-tests/tests/postgres/test_psycopg_functional.py +++ b/tests/opentelemetry-docker-tests/tests/postgres/test_psycopg_functional.py @@ -19,7 +19,16 @@ from opentelemetry import trace as trace_api from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_STATEMENT, + DB_SYSTEM, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from opentelemetry.test.test_base import TestBase POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost") @@ -67,21 +76,11 @@ def validate_spans(self, span_name): self.assertIsNotNone(child_span.parent) self.assertIs(child_span.parent, root_span.get_span_context()) self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_SYSTEM], "postgresql" - ) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_NAME], POSTGRES_DB_NAME - ) - self.assertEqual( - child_span.attributes[SpanAttributes.DB_USER], POSTGRES_USER - ) - self.assertEqual( - child_span.attributes[SpanAttributes.NET_PEER_NAME], POSTGRES_HOST - ) - self.assertEqual( - child_span.attributes[SpanAttributes.NET_PEER_PORT], POSTGRES_PORT - ) + self.assertEqual(child_span.attributes[DB_SYSTEM], "postgresql") + self.assertEqual(child_span.attributes[DB_NAME], POSTGRES_DB_NAME) + self.assertEqual(child_span.attributes[DB_USER], POSTGRES_USER) + self.assertEqual(child_span.attributes[NET_PEER_NAME], POSTGRES_HOST) + self.assertEqual(child_span.attributes[NET_PEER_PORT], POSTGRES_PORT) def test_execute(self): """Should create a child span for execute method""" @@ -148,7 +147,7 @@ def test_composed_queries(self): span = spans[2] self.assertEqual(span.name, "SELECT") self.assertEqual( - span.attributes[SpanAttributes.DB_STATEMENT], + span.attributes[DB_STATEMENT], 'SELECT FROM "users" where "name"=\'"abc"\'', ) @@ -172,6 +171,6 @@ def test_commenter_enabled(self): span = spans[2] self.assertEqual(span.name, "SELECT") self.assertEqual( - span.attributes[SpanAttributes.DB_STATEMENT], + span.attributes[DB_STATEMENT], 'SELECT FROM "users" where "name"=\'"abc"\'', ) diff --git a/tests/opentelemetry-docker-tests/tests/pymongo/test_pymongo_functional.py b/tests/opentelemetry-docker-tests/tests/pymongo/test_pymongo_functional.py index c506d0452a..94745a7692 100644 --- a/tests/opentelemetry-docker-tests/tests/pymongo/test_pymongo_functional.py +++ b/tests/opentelemetry-docker-tests/tests/pymongo/test_pymongo_functional.py @@ -18,7 +18,15 @@ from opentelemetry import trace as trace_api from opentelemetry.instrumentation.pymongo import PymongoInstrumentor -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_MONGODB_COLLECTION, + DB_NAME, + DB_STATEMENT, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from opentelemetry.test.test_base import TestBase MONGODB_HOST = os.getenv("MONGODB_HOST", "localhost") @@ -60,21 +68,15 @@ def validate_spans(self, expected_db_statement): self.assertIsNotNone(pymongo_span.parent) self.assertIs(pymongo_span.parent, root_span.get_span_context()) self.assertIs(pymongo_span.kind, trace_api.SpanKind.CLIENT) + self.assertEqual(pymongo_span.attributes[DB_NAME], MONGODB_DB_NAME) + self.assertEqual(pymongo_span.attributes[NET_PEER_NAME], MONGODB_HOST) + self.assertEqual(pymongo_span.attributes[NET_PEER_PORT], MONGODB_PORT) self.assertEqual( - pymongo_span.attributes[SpanAttributes.DB_NAME], MONGODB_DB_NAME - ) - self.assertEqual( - pymongo_span.attributes[SpanAttributes.NET_PEER_NAME], MONGODB_HOST - ) - self.assertEqual( - pymongo_span.attributes[SpanAttributes.NET_PEER_PORT], MONGODB_PORT - ) - self.assertEqual( - pymongo_span.attributes[SpanAttributes.DB_MONGODB_COLLECTION], + pymongo_span.attributes[DB_MONGODB_COLLECTION], MONGODB_COLLECTION_NAME, ) self.assertEqual( - pymongo_span.attributes[SpanAttributes.DB_STATEMENT], + pymongo_span.attributes[DB_STATEMENT], expected_db_statement, ) diff --git a/tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py b/tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py index 537f42377c..e33b6c0f88 100644 --- a/tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py +++ b/tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py @@ -18,7 +18,15 @@ from opentelemetry import trace as trace_api from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_SYSTEM, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from opentelemetry.test.test_base import TestBase MYSQL_USER = os.getenv("MYSQL_USER", "testuser") @@ -65,19 +73,11 @@ def validate_spans(self, span_name): self.assertIsNotNone(db_span.parent) self.assertIs(db_span.parent, root_span.get_span_context()) self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual(db_span.attributes[SpanAttributes.DB_SYSTEM], "mysql") - self.assertEqual( - db_span.attributes[SpanAttributes.DB_NAME], MYSQL_DB_NAME - ) - self.assertEqual( - db_span.attributes[SpanAttributes.DB_USER], MYSQL_USER - ) - self.assertEqual( - db_span.attributes[SpanAttributes.NET_PEER_NAME], MYSQL_HOST - ) - self.assertEqual( - db_span.attributes[SpanAttributes.NET_PEER_PORT], MYSQL_PORT - ) + self.assertEqual(db_span.attributes[DB_SYSTEM], "mysql") + self.assertEqual(db_span.attributes[DB_NAME], MYSQL_DB_NAME) + self.assertEqual(db_span.attributes[DB_USER], MYSQL_USER) + self.assertEqual(db_span.attributes[NET_PEER_NAME], MYSQL_HOST) + self.assertEqual(db_span.attributes[NET_PEER_PORT], MYSQL_PORT) def test_execute(self): """Should create a child span for execute""" diff --git a/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py b/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py index bfb3aa1f48..ed79103369 100644 --- a/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py +++ b/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py @@ -27,7 +27,14 @@ from opentelemetry import trace from opentelemetry.instrumentation.redis import RedisInstrumentor -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_REDIS_DATABASE_INDEX, + DB_STATEMENT, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from opentelemetry.test.test_base import TestBase @@ -45,13 +52,9 @@ def tearDown(self): def _check_span(self, span, name): self.assertEqual(span.name, name) self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_REDIS_DATABASE_INDEX), 0 - ) - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_NAME], "localhost" - ) - self.assertEqual(span.attributes[SpanAttributes.NET_PEER_PORT], 6379) + self.assertEqual(span.attributes.get(DB_REDIS_DATABASE_INDEX), 0) + self.assertEqual(span.attributes[NET_PEER_NAME], "localhost") + self.assertEqual(span.attributes[NET_PEER_PORT], 6379) def test_long_command_sanitized(self): RedisInstrumentor().uninstrument() @@ -64,13 +67,9 @@ def test_long_command_sanitized(self): span = spans[0] self._check_span(span, "MGET") self.assertTrue( - span.attributes.get(SpanAttributes.DB_STATEMENT).startswith( - "MGET ? ? ? ?" - ) - ) - self.assertTrue( - span.attributes.get(SpanAttributes.DB_STATEMENT).endswith("...") + span.attributes.get(DB_STATEMENT).startswith("MGET ? ? ? ?") ) + self.assertTrue(span.attributes.get(DB_STATEMENT).endswith("...")) def test_long_command(self): self.redis_client.mget(*range(1000)) @@ -80,13 +79,9 @@ def test_long_command(self): span = spans[0] self._check_span(span, "MGET") self.assertTrue( - span.attributes.get(SpanAttributes.DB_STATEMENT).startswith( - "MGET ? ? ? ?" - ) - ) - self.assertTrue( - span.attributes.get(SpanAttributes.DB_STATEMENT).endswith("...") + span.attributes.get(DB_STATEMENT).startswith("MGET ? ? ? ?") ) + self.assertTrue(span.attributes.get(DB_STATEMENT).endswith("...")) def test_basics_sanitized(self): RedisInstrumentor().uninstrument() @@ -97,9 +92,7 @@ def test_basics_sanitized(self): self.assertEqual(len(spans), 1) span = spans[0] self._check_span(span, "GET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "GET ?") self.assertEqual(span.attributes.get("db.redis.args_length"), 2) def test_basics(self): @@ -108,9 +101,7 @@ def test_basics(self): self.assertEqual(len(spans), 1) span = spans[0] self._check_span(span, "GET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "GET ?") self.assertEqual(span.attributes.get("db.redis.args_length"), 2) def test_pipeline_traced_sanitized(self): @@ -128,7 +119,7 @@ def test_pipeline_traced_sanitized(self): span = spans[0] self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -145,7 +136,7 @@ def test_pipeline_traced(self): span = spans[0] self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -165,9 +156,7 @@ def test_pipeline_immediate_sanitized(self): self.assertEqual(len(spans), 2) span = spans[0] self._check_span(span, "SET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "SET ? ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "SET ? ?") def test_pipeline_immediate(self): with self.redis_client.pipeline() as pipeline: @@ -181,9 +170,7 @@ def test_pipeline_immediate(self): self.assertEqual(len(spans), 2) span = spans[0] self._check_span(span, "SET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "SET ? ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "SET ? ?") def test_parent(self): """Ensure OpenTelemetry works with redis.""" @@ -229,9 +216,7 @@ def test_basics(self): self.assertEqual(len(spans), 1) span = spans[0] self._check_span(span, "GET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "GET ?") self.assertEqual(span.attributes.get("db.redis.args_length"), 2) def test_pipeline_traced(self): @@ -246,7 +231,7 @@ def test_pipeline_traced(self): span = spans[0] self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -291,13 +276,9 @@ def tearDown(self): def _check_span(self, span, name): self.assertEqual(span.name, name) self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_REDIS_DATABASE_INDEX), 0 - ) - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_NAME], "localhost" - ) - self.assertEqual(span.attributes[SpanAttributes.NET_PEER_PORT], 6379) + self.assertEqual(span.attributes.get(DB_REDIS_DATABASE_INDEX), 0) + self.assertEqual(span.attributes[NET_PEER_NAME], "localhost") + self.assertEqual(span.attributes[NET_PEER_PORT], 6379) def test_long_command(self): async_call(self.redis_client.mget(*range(1000))) @@ -307,13 +288,9 @@ def test_long_command(self): span = spans[0] self._check_span(span, "MGET") self.assertTrue( - span.attributes.get(SpanAttributes.DB_STATEMENT).startswith( - "MGET ? ? ? ?" - ) - ) - self.assertTrue( - span.attributes.get(SpanAttributes.DB_STATEMENT).endswith("...") + span.attributes.get(DB_STATEMENT).startswith("MGET ? ? ? ?") ) + self.assertTrue(span.attributes.get(DB_STATEMENT).endswith("...")) def test_basics(self): self.assertIsNone(async_call(self.redis_client.get("cheese"))) @@ -321,9 +298,7 @@ def test_basics(self): self.assertEqual(len(spans), 1) span = spans[0] self._check_span(span, "GET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "GET ?") self.assertEqual(span.attributes.get("db.redis.args_length"), 2) def test_execute_command_traced_full_time(self): @@ -366,7 +341,7 @@ async def pipeline_simple(): span = spans[0] self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -415,9 +390,7 @@ async def pipeline_immediate(): self.assertEqual(len(spans), 2) span = spans[0] self._check_span(span, "SET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "SET ? ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "SET ? ?") def test_pipeline_immediate_traced_full_time(self): """Command should be traced for coroutine execution time, not creation time.""" @@ -490,9 +463,7 @@ def test_basics(self): self.assertEqual(len(spans), 1) span = spans[0] self._check_span(span, "GET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "GET ?") self.assertEqual(span.attributes.get("db.redis.args_length"), 2) def test_execute_command_traced_full_time(self): @@ -535,7 +506,7 @@ async def pipeline_simple(): span = spans[0] self._check_span(span, "SET RPUSH HGETALL") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SET ? ?\nRPUSH ? ?\nHGETALL ?", ) self.assertEqual(span.attributes.get("db.redis.pipeline_length"), 3) @@ -604,13 +575,9 @@ def tearDown(self): def _check_span(self, span, name): self.assertEqual(span.name, name) self.assertIs(span.status.status_code, trace.StatusCode.UNSET) - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_NAME], "localhost" - ) - self.assertEqual(span.attributes[SpanAttributes.NET_PEER_PORT], 6379) - self.assertEqual( - span.attributes[SpanAttributes.DB_REDIS_DATABASE_INDEX], 10 - ) + self.assertEqual(span.attributes[NET_PEER_NAME], "localhost") + self.assertEqual(span.attributes[NET_PEER_PORT], 6379) + self.assertEqual(span.attributes[DB_REDIS_DATABASE_INDEX], 10) def test_get(self): self.assertIsNone(self.redis_client.get("foo")) @@ -618,9 +585,7 @@ def test_get(self): self.assertEqual(len(spans), 1) span = spans[0] self._check_span(span, "GET") - self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), "GET ?" - ) + self.assertEqual(span.attributes.get(DB_STATEMENT), "GET ?") class TestRedisearchInstrument(TestBase): diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/mixins.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/mixins.py index 57317b76c6..2fda993141 100644 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/mixins.py +++ b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/mixins.py @@ -22,7 +22,11 @@ from opentelemetry import trace from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_STATEMENT, + DB_SYSTEM, +) from opentelemetry.test.test_base import TestBase Base = declarative_base() @@ -115,9 +119,7 @@ def _check_span(self, span, name): if self.SQL_DB: name = f"{name} {self.SQL_DB}" self.assertEqual(span.name, name) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_NAME), self.SQL_DB - ) + self.assertEqual(span.attributes.get(DB_NAME), self.SQL_DB) self.assertIs(span.status.status_code, trace.StatusCode.UNSET) self.assertGreater((span.end_time - span.start_time), 0) @@ -132,14 +134,14 @@ def test_orm_insert(self): self.assertEqual(len(spans), 2) span = spans[1] stmt = "INSERT INTO players (id, name) VALUES " - if span.attributes.get(SpanAttributes.DB_SYSTEM) == "sqlite": + if span.attributes.get(DB_SYSTEM) == "sqlite": stmt += "(?, ?)" else: stmt += "(%(id)s, %(name)s)" self._check_span(span, "INSERT") self.assertIn( "INSERT INTO players", - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), ) self.check_meta(span) @@ -153,14 +155,14 @@ def test_session_query(self): self.assertEqual(len(spans), 2) span = spans[1] stmt = "SELECT players.id AS players_id, players.name AS players_name \nFROM players \nWHERE players.name = " - if span.attributes.get(SpanAttributes.DB_SYSTEM) == "sqlite": + if span.attributes.get(DB_SYSTEM) == "sqlite": stmt += "?" else: stmt += "%(name_1)s" self._check_span(span, "SELECT") self.assertIn( "SELECT players.id AS players_id, players.name AS players_name \nFROM players \nWHERE players.name", - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), ) self.check_meta(span) @@ -177,7 +179,7 @@ def test_engine_connect_execute(self): span = spans[1] self._check_span(span, "SELECT") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SELECT * FROM players", ) self.check_meta(span) diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mssql.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mssql.py index b92228c5cd..ce2feeaf2d 100644 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mssql.py +++ b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mssql.py @@ -18,7 +18,15 @@ from sqlalchemy.exc import ProgrammingError from opentelemetry import trace -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_STATEMENT, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from .mixins import Player, SQLAlchemyTestMixin @@ -48,20 +56,18 @@ class MssqlConnectorTestCase(SQLAlchemyTestMixin): def check_meta(self, span): # check database connection tags self.assertEqual( - span.attributes.get(SpanAttributes.NET_PEER_NAME), + span.attributes.get(NET_PEER_NAME), MSSQL_CONFIG["host"], ) self.assertEqual( - span.attributes.get(SpanAttributes.NET_PEER_PORT), + span.attributes.get(NET_PEER_PORT), MSSQL_CONFIG["port"], ) self.assertEqual( - span.attributes.get(SpanAttributes.DB_NAME), + span.attributes.get(DB_NAME), MSSQL_CONFIG["database"], ) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_USER), MSSQL_CONFIG["user"] - ) + self.assertEqual(span.attributes.get(DB_USER), MSSQL_CONFIG["user"]) def test_engine_execute_errors(self): # ensures that SQL errors are reported @@ -77,12 +83,10 @@ def test_engine_execute_errors(self): # span fields self.assertEqual(span.name, "SELECT opentelemetry-tests") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SELECT * FROM a_wrong_table", ) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_NAME), self.SQL_DB - ) + self.assertEqual(span.attributes.get(DB_NAME), self.SQL_DB) self.check_meta(span) self.assertTrue(span.end_time - span.start_time > 0) # check the error @@ -106,6 +110,6 @@ def test_orm_insert(self): self._check_span(span, "INSERT") self.assertIn( "INSERT INTO players", - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), ) self.check_meta(span) diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mysql.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mysql.py index 6e4c85cd92..a6e259c103 100644 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mysql.py +++ b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mysql.py @@ -18,7 +18,15 @@ from sqlalchemy.exc import ProgrammingError from opentelemetry import trace -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_STATEMENT, + DB_USER, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from .mixins import SQLAlchemyTestMixin @@ -46,20 +54,18 @@ class MysqlConnectorTestCase(SQLAlchemyTestMixin): def check_meta(self, span): # check database connection tags self.assertEqual( - span.attributes.get(SpanAttributes.NET_PEER_NAME), + span.attributes.get(NET_PEER_NAME), MYSQL_CONFIG["host"], ) self.assertEqual( - span.attributes.get(SpanAttributes.NET_PEER_PORT), + span.attributes.get(NET_PEER_PORT), MYSQL_CONFIG["port"], ) self.assertEqual( - span.attributes.get(SpanAttributes.DB_NAME), + span.attributes.get(DB_NAME), MYSQL_CONFIG["database"], ) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_USER), MYSQL_CONFIG["user"] - ) + self.assertEqual(span.attributes.get(DB_USER), MYSQL_CONFIG["user"]) def test_engine_execute_errors(self): # ensures that SQL errors are reported @@ -75,12 +81,10 @@ def test_engine_execute_errors(self): # span fields self.assertEqual(span.name, "SELECT opentelemetry-tests") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SELECT * FROM a_wrong_table", ) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_NAME), self.SQL_DB - ) + self.assertEqual(span.attributes.get(DB_NAME), self.SQL_DB) self.check_meta(span) self.assertTrue(span.end_time - span.start_time > 0) # check the error diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_postgres.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_postgres.py index d0b659ec8b..a384bbe8cc 100644 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_postgres.py +++ b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_postgres.py @@ -19,7 +19,14 @@ from sqlalchemy.exc import ProgrammingError from opentelemetry import trace -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_STATEMENT, +) +from opentelemetry.semconv._incubating.attributes.net_attributes import ( + NET_PEER_NAME, + NET_PEER_PORT, +) from .mixins import SQLAlchemyTestMixin @@ -49,11 +56,11 @@ class PostgresTestCase(SQLAlchemyTestMixin): def check_meta(self, span): # check database connection tags self.assertEqual( - span.attributes.get(SpanAttributes.NET_PEER_NAME), + span.attributes.get(NET_PEER_NAME), POSTGRES_CONFIG["host"], ) self.assertEqual( - span.attributes.get(SpanAttributes.NET_PEER_PORT), + span.attributes.get(NET_PEER_PORT), POSTGRES_CONFIG["port"], ) @@ -70,12 +77,10 @@ def test_engine_execute_errors(self): # span fields self.assertEqual(span.name, "SELECT opentelemetry-tests") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SELECT * FROM a_wrong_table", ) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_NAME), self.SQL_DB - ) + self.assertEqual(span.attributes.get(DB_NAME), self.SQL_DB) self.check_meta(span) self.assertTrue(span.end_time - span.start_time > 0) # check the error diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_sqlite.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_sqlite.py index 884e3a37c2..357970ba77 100644 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_sqlite.py +++ b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_sqlite.py @@ -16,7 +16,10 @@ from sqlalchemy.exc import OperationalError from opentelemetry import trace -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes.db_attributes import ( + DB_NAME, + DB_STATEMENT, +) from .mixins import SQLAlchemyTestMixin @@ -44,12 +47,10 @@ def test_engine_execute_errors(self): # span fields self.assertEqual(span.name, "SELECT :memory:") self.assertEqual( - span.attributes.get(SpanAttributes.DB_STATEMENT), + span.attributes.get(DB_STATEMENT), "SELECT * FROM a_wrong_table", ) - self.assertEqual( - span.attributes.get(SpanAttributes.DB_NAME), self.SQL_DB - ) + self.assertEqual(span.attributes.get(DB_NAME), self.SQL_DB) self.assertTrue((span.end_time - span.start_time) > 0) # check the error self.assertIs( diff --git a/tests/opentelemetry-docker-tests/tests/test-requirements.txt b/tests/opentelemetry-docker-tests/tests/test-requirements.txt index 048d35cb4c..434f44d597 100644 --- a/tests/opentelemetry-docker-tests/tests/test-requirements.txt +++ b/tests/opentelemetry-docker-tests/tests/test-requirements.txt @@ -4,20 +4,19 @@ asgiref==3.8.1 async-timeout==4.0.3 asyncpg==0.29.0 attrs==23.2.0 -bcrypt==4.1.2 +bcrypt==5.0.0 billiard==4.2.0 celery==5.3.6 certifi==2024.7.4 -cffi==1.16.0 +cffi==2.0.0 chardet==3.0.4 click==8.1.7 click-didyoumean==0.3.0 click-plugins==1.1.1 click-repl==0.3.0 -cryptography==44.0.1 -Deprecated==1.2.14 +cryptography==45.0.7 distro==1.9.0 -Django==4.2.17 +Django==4.2.29 dnspython==2.6.1 docker==5.0.3 docker-compose==1.29.2 @@ -34,23 +33,21 @@ jsonschema==3.2.0 kombu==5.3.5 mysql-connector-python==8.3.0 mysqlclient==2.1.1 -opencensus-proto==0.1.0 packaging==24.0 -paramiko==3.4.0 +paramiko==4.0.0 pluggy==1.4.0 prometheus_client==0.20.0 prompt-toolkit==3.0.43 -protobuf==3.20.3 # prerequisite: install libpq-dev (debian) or postgresql-devel (rhel), postgresql (mac) # see https://www.psycopg.org/docs/install.html#build-prerequisites # you might have to install additional packages depending on your OS psycopg==3.1.18 psycopg2==2.9.9 psycopg2-binary==2.9.9 -pycparser==2.21 +pycparser==3.0 pymongo==4.6.3 PyMySQL==0.10.1 -PyNaCl==1.5.0 +PyNaCl==1.6.2 # prerequisite: install unixodbc pyodbc==5.0.1 pyrsistent==0.20.0 @@ -61,7 +58,6 @@ python-dotenv==0.21.1 pytz==2024.1 PyYAML==5.3.1 redis==5.0.1 -remoulade==3.2.0 requests==2.25.0 six==1.16.0 SQLAlchemy==1.4.52 diff --git a/tox.ini b/tox.ini index 350584e85a..1f2ca7f8e9 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,8 @@ envlist = ; for specifying supported Python versions per package. ; instrumentation-openai - py3{9,10,11,12,13,14}-test-instrumentation-openai-v2-{oldest,latest} + py3{10,11,12,13,14}-test-instrumentation-openai-v2-{oldest,latest} + py313-test-instrumentation-openai-v2-pydantic1 pypy3-test-instrumentation-openai-v2-{oldest,latest} lint-instrumentation-openai-v2 @@ -18,19 +19,19 @@ envlist = lint-instrumentation-openai_agents-v2 ; instrumentation-vertexai - py3{9,10,11,12,13,14}-test-instrumentation-vertexai-{oldest,latest} + py3{10,11,12,13,14}-test-instrumentation-vertexai-{oldest,latest} # Disabling pypy3 as shapely does not have wheels and fails to compile # pypy3-test-instrumentation-vertexai-{oldest,latest} lint-instrumentation-vertexai ; instrumentation-google-genai - py3{9,10,11,12,13,14}-test-instrumentation-google-genai-{oldest,latest} + py3{10,11,12,13,14}-test-instrumentation-google-genai-{oldest,latest} # Disabling pypy3 as shapely does not have wheels and fails to compile # pypy3-test-instrumentation-google-genai-{0,1} lint-instrumentation-google-genai ; instrumentation-anthropic - py3{9,10,11,12,13,14}-test-instrumentation-anthropic-{oldest,latest} + py3{10,11,12,13,14}-test-instrumentation-anthropic-{oldest,latest} # Disabling pypy3 as jiter (anthropic dep) requires PyPy 3.11+ # pypy3-test-instrumentation-anthropic-{oldest,latest} lint-instrumentation-anthropic @@ -42,59 +43,60 @@ envlist = lint-instrumentation-claude-agent-sdk ; opentelemetry-resource-detector-containerid - py3{9,10,11,12,13,14}-test-resource-detector-containerid + py3{10,11,12,13,14}-test-resource-detector-containerid pypy3-test-resource-detector-containerid lint-resource-detector-containerid ; opentelemetry-resource-detector-azure - py3{9,10,11,12,13,14}-test-resource-detector-azure-{0,1} + py3{10,11,12,13,14}-test-resource-detector-azure-{0,1} pypy3-test-resource-detector-azure-{0,1} lint-resource-detector-azure ; opentelemetry-sdk-extension-aws - py3{9,10,11,12,13,14}-test-sdk-extension-aws-{0,1} + py3{10,11,12,13,14}-test-sdk-extension-aws-{0,1} pypy3-test-sdk-extension-aws-{0,1} lint-sdk-extension-aws benchmark-sdk-extension-aws ; opentelemetry-distro - py3{9,10,11,12,13,14}-test-distro + py3{10,11,12,13,14}-test-distro pypy3-test-distro lint-distro ; opentelemetry-instrumentation - py3{9,10,11,12,13,14}-test-opentelemetry-instrumentation - pypy3-test-opentelemetry-instrumentation + py3{10,11,12,13,14}-test-opentelemetry-instrumentation-{wrapt1,wrapt2} + pypy3-test-opentelemetry-instrumentation-wrapt2 lint-opentelemetry-instrumentation ; opentelemetry-instrumentation-aiohttp-client - py3{9,10,11,12,13,14}-test-instrumentation-aiohttp-client + py3{10,11,12,13,14}-test-instrumentation-aiohttp-client pypy3-test-instrumentation-aiohttp-client lint-instrumentation-aiohttp-client ; opentelemetry-instrumentation-aiohttp-server - py3{9,10,11,12,13,14}-test-instrumentation-aiohttp-server + py3{10,11,12,13,14}-test-instrumentation-aiohttp-server pypy3-test-instrumentation-aiohttp-server lint-instrumentation-aiohttp-server ; opentelemetry-instrumentation-aiopg - py3{9,10,11,12,13,14}-test-instrumentation-aiopg + py3{10,11,12,13,14}-test-instrumentation-aiopg-{wrapt1,wrapt2} ; instrumentation-aiopg intentionally excluded from pypy3 lint-instrumentation-aiopg ; opentelemetry-instrumentation-aws-lambda - py3{9,10,11,12,13,14}-test-instrumentation-aws-lambda + py3{10,11,12,13,14}-test-instrumentation-aws-lambda pypy3-test-instrumentation-aws-lambda lint-instrumentation-aws-lambda ; opentelemetry-instrumentation-botocore - py3{9,10,11,12,13,14}-test-instrumentation-botocore-{0,1} + py3{10,11,12,13,14}-test-instrumentation-botocore-{0,1}-{wrapt1,wrapt2} + py3{10,11,12,13,14}-test-instrumentation-botocore-{2,3} ; FIXME: see https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1736 ; pypy3-test-instrumentation-botocore lint-instrumentation-botocore ; opentelemetry-instrumentation-boto3sqs - py3{9,10,11,12,13,14}-test-instrumentation-boto3sqs + py3{10,11,12,13,14}-test-instrumentation-boto3sqs pypy3-test-instrumentation-boto3sqs lint-instrumentation-boto3sqs @@ -106,32 +108,25 @@ envlist = ; below mean these dependencies are being used: ; 0: django~=2.0 ; 1: django~=3.0 - ; 2: django>=4.0b1,<5.0 backports.zoneinfo==0.2.1 + ; 2: django~=4.0 ; 3: django>=5.2<6.0 - py3{9}-test-instrumentation-django-{0,1,2} - py3{10,11,12}-test-instrumentation-django-{1,3} + py3{10,11,12}-test-instrumentation-django-{0,1,2,3} py3{13,14}-test-instrumentation-django-3 - pypy3-test-instrumentation-django-{0,1} + pypy3-test-instrumentation-django-{0,1,2,3} lint-instrumentation-django ; opentelemetry-instrumentation-dbapi - py3{9,10,11,12,13,14}-test-instrumentation-dbapi - pypy3-test-instrumentation-dbapi + py3{10,11,12,13,14}-test-instrumentation-dbapi-{wrapt1,wrapt2} + pypy3-test-instrumentation-dbapi-wrapt2 lint-instrumentation-dbapi - ; opentelemetry-instrumentation-boto - py3{9,10,11}-test-instrumentation-boto - ; FIXME: see https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1736 - ; pypy3-test-instrumentation-boto - lint-instrumentation-boto - ; opentelemetry-instrumentation-asyncclick - py3{9,10,11,12,13,14}-test-instrumentation-asyncclick + py3{10,11,12,13,14}-test-instrumentation-asyncclick pypy3-test-instrumentation-asyncclick lint-instrumentation-asyncclick ; opentelemetry-instrumentation-click - py3{9,10,11,12,13,14}-test-instrumentation-click + py3{10,11,12,13,14}-test-instrumentation-click pypy3-test-instrumentation-click lint-instrumentation-click @@ -141,28 +136,25 @@ envlist = ; 0: elasticsearch-dsl==6.4.0 elasticsearch==6.8.2 ; 1: elasticsearch-dsl==7.4.1 elasticsearch==7.17.9 ; 2: elasticsearch-dsl==8.13.1 elasticsearch==8.13.1 - py3{9,10,11,12,13,14}-test-instrumentation-elasticsearch-{0,1,2} + py3{10,11,12,13,14}-test-instrumentation-elasticsearch-{0,1,2} pypy3-test-instrumentation-elasticsearch-{0,1,2} lint-instrumentation-elasticsearch ; opentelemetry-instrumentation-falcon - ; py310 does not work with falcon 1 - ; py3{9} will be dropped for falcon 4 + ; TODO: py310 does not work with falcon 1 - Drop requirements-0.txt and bump supported version to >2 ; The numbers at the end of the environment names ; below mean these dependencies are being used: - ; 0: falcon ==1.4.1 ; 1: falcon >=2.0.0,<3.0.0 ; 2: falcon >=3.0.0,<3.1.2 ; 3: falcon >=3.1.2,<4.0.0 ; 4: falcon >=4.0.0,<5.0.0 - py3{9}-test-instrumentation-falcon-{0,1,2,3} py3{10,11,12}-test-instrumentation-falcon-{1,2,3,4} py3{13,14}-test-instrumentation-falcon-4 - pypy3-test-instrumentation-falcon-{0,1,2,3,4} + pypy3-test-instrumentation-falcon-{1,2,3,4} lint-instrumentation-falcon ; opentelemetry-instrumentation-fastapi - py3{9,10,11,12,13,14}-test-instrumentation-fastapi + py3{10,11,12,13,14}-test-instrumentation-fastapi pypy3-test-instrumentation-fastapi lint-instrumentation-fastapi @@ -173,12 +165,12 @@ envlist = ; 1: Flask ==2.2.0 Werkzeug <3.0.0 ; 2: Flask >=3.0.0 Werkzeug >=3.0.0 ; 3: Flask >=3.1.0 Werkzeug >=3.1.0 - py3{9,10,11,12,13,14}-test-instrumentation-flask-{0,1,2,3} + py3{10,11,12,13,14}-test-instrumentation-flask-{0,1,2,3} pypy3-test-instrumentation-flask-{0,1} lint-instrumentation-flask ; opentelemetry-instrumentation-urllib - py3{9,10,11,12,13,14}-test-instrumentation-urllib + py3{10,11,12,13,14}-test-instrumentation-urllib pypy3-test-instrumentation-urllib lint-instrumentation-urllib @@ -188,40 +180,40 @@ envlist = ; 0: urllib3 >=1.0.0,<2.0.0 ; 1: urllib3 >=2.0.0,<3.0.0 ; TODO #4200 Add py314 testing after replacing httpretty -- problem with https tests in 3.14 - py3{9,10,11,12,13}-test-instrumentation-urllib3-{0,1} + py3{10,11,12,13}-test-instrumentation-urllib3-{0,1} pypy3-test-instrumentation-urllib3-{0,1} lint-instrumentation-urllib3 ; opentelemetry-instrumentation-requests - py3{9,10,11,12,13,14}-test-instrumentation-requests + py3{10,11,12,13,14}-test-instrumentation-requests ;pypy3-test-instrumentation-requests lint-instrumentation-requests ; opentelemetry-instrumentation-starlette - py3{9,10,11,12,13,14}-test-instrumentation-starlette-{oldest,latest} + py3{10,11,12,13,14}-test-instrumentation-starlette-{oldest,latest} pypy3-test-instrumentation-starlette-{oldest,latest} lint-instrumentation-starlette ; opentelemetry-instrumentation-jinja2 - py3{9,10,11,12,13,14}-test-instrumentation-jinja2 + py3{10,11,12,13,14}-test-instrumentation-jinja2 pypy3-test-instrumentation-jinja2 lint-instrumentation-jinja2 ; opentelemetry-instrumentation-logging - py3{9,10,11,12,13,14}-test-instrumentation-logging + py3{10,11,12,13,14}-test-instrumentation-logging pypy3-test-instrumentation-logging lint-instrumentation-logging benchmark-instrumentation-logging ; opentelemetry-exporter-richconsole - py3{9,10,11,12,13,14}-test-exporter-richconsole + py3{10,11,12,13,14}-test-exporter-richconsole pypy3-test-exporter-richconsole lint-exporter-richconsole ; opentelemetry-exporter-prometheus-remote-write - py3{9,10,11,12,13,14}-test-exporter-prometheus-remote-write + py3{10,11,12,13,14}-test-exporter-prometheus-remote-write # excluded from pypy3 due to missing wheel - pypy310-test-exporter-prometheus-remote-write + pypy3-test-exporter-prometheus-remote-write lint-exporter-prometheus-remote-write ; opentelemetry-instrumentation-mysql @@ -229,12 +221,12 @@ envlist = ; below mean these dependencies are being used: ; 0: mysql-connector-python >=8.0.0,<9.0.0 ; 1: mysql-connector-python ~=9.0.0 - py3{9,10,11,12,13,14}-test-instrumentation-mysql-{0,1} + py3{10,11,12,13,14}-test-instrumentation-mysql-{0,1} pypy3-test-instrumentation-mysql-{0,1} lint-instrumentation-mysql ; opentelemetry-instrumentation-mysqlclient - py3{9,10,11,12,13,14}-test-instrumentation-mysqlclient + py3{10,11,12,13,14}-test-instrumentation-mysqlclient pypy3-test-instrumentation-mysqlclient ; prerequisite: follow the instructions here ; https://github.com/PyMySQL/mysqlclient#install @@ -242,13 +234,13 @@ envlist = lint-instrumentation-mysqlclient ; opentelemetry-instrumentation-psycopg2 - py3{9,10,11,12,13,14}-test-instrumentation-psycopg2 - py3{9,10,11,12,13,14}-test-instrumentation-psycopg2-binary + py3{10,11,12,13,14}-test-instrumentation-psycopg2 + py3{10,11,12,13,14}-test-instrumentation-psycopg2-binary ; ext-psycopg2 intentionally excluded from pypy3 lint-instrumentation-psycopg2 ; opentelemetry-instrumentation-psycopg - py3{9,10,11,12,13,14}-test-instrumentation-psycopg + py3{10,11,12,13,14}-test-instrumentation-psycopg pypy3-test-instrumentation-psycopg lint-instrumentation-psycopg @@ -260,48 +252,48 @@ envlist = ; 2: pymemcache >3.0.0,<3.4.2 ; 3: pymemcache ==3.4.2 ; 4: pymemcache ==4.0.0 - py3{9,10,11,12,13,14}-test-instrumentation-pymemcache-{0,1,2,3,4} + py3{10,11,12,13,14}-test-instrumentation-pymemcache-{0,1,2,3,4} pypy3-test-instrumentation-pymemcache-{0,1,2,3,4} lint-instrumentation-pymemcache ; opentelemetry-instrumentation-pymongo - py3{9,10,11,12,13,14}-test-instrumentation-pymongo + py3{10,11,12,13,14}-test-instrumentation-pymongo pypy3-test-instrumentation-pymongo lint-instrumentation-pymongo ; opentelemetry-instrumentation-pymysql - py3{9,10,11,12,13,14}-test-instrumentation-pymysql + py3{10,11,12,13,14}-test-instrumentation-pymysql pypy3-test-instrumentation-pymysql lint-instrumentation-pymysql ; opentelemetry-instrumentation-pymssql - py3{9,10,11,12,13,14}-test-instrumentation-pymssql + py3{10,11,12,13,14}-test-instrumentation-pymssql ; pymssql has no support for pypy: see https://github.com/pymssql/pymssql/pull/517 ; pypy3-test-instrumentation-pymssql lint-instrumentation-pymssql ; opentelemetry-instrumentation-pyramid - py3{9,10,11,12,13,14}-test-instrumentation-pyramid + py3{10,11,12,13,14}-test-instrumentation-pyramid pypy3-test-instrumentation-pyramid lint-instrumentation-pyramid ; opentelemetry-instrumentation-asgi - py3{9,10,11,12,13,14}-test-instrumentation-asgi + py3{10,11,12,13,14}-test-instrumentation-asgi pypy3-test-instrumentation-asgi lint-instrumentation-asgi ; opentelemetry-instrumentation-asyncpg - py3{9,10,11,12,13,14}-test-instrumentation-asyncpg + py3{10,11,12,13,14}-test-instrumentation-asyncpg-{wrapt1,wrapt2} ; ext-asyncpg intentionally excluded from pypy3 lint-instrumentation-asyncpg ; opentelemetry-instrumentation-sqlite3 - py3{9,10,11,12,13,14}-test-instrumentation-sqlite3 + py3{10,11,12,13,14}-test-instrumentation-sqlite3 pypy3-test-instrumentation-sqlite3 lint-instrumentation-sqlite3 ; opentelemetry-instrumentation-wsgi - py3{9,10,11,12,13,14}-test-instrumentation-wsgi + py3{10,11,12,13,14}-test-instrumentation-wsgi pypy3-test-instrumentation-wsgi lint-instrumentation-wsgi @@ -311,53 +303,53 @@ envlist = ; 0: grpcio==1.62.0 ; 1: grpcio==1.75.1 ; TODO #4201 simplify tests-requirements here to use latest|oldest testing pattern - py3{9,10,11,12}-test-instrumentation-grpc-{0,1} - py3{13,14}-test-instrumentation-grpc-1 + py3{10,11,12}-test-instrumentation-grpc-{0,1}-{wrapt1,wrapt2} + py3{13,14}-test-instrumentation-grpc-1-{wrapt1,wrapt2} lint-instrumentation-grpc ; opentelemetry-instrumentation-sqlalchemy ; The numbers at the end of the environment names ; below mean these dependencies are being used: - ; 0: sqlalchemy>=1.1,<1.2 + ; 0: sqlalchemy>=1.1,<1.2 - TODO: drop test-requirements-0.txt and update to >=1.4,<2.0 once 1.1 support is removed ; 1: sqlalchemy~=1.4 aiosqlite ; 2: sqlalchemy~=2.0.0 - py3{9,10,11,12,13}-test-instrumentation-sqlalchemy-{1,2} + py3{10,11,12,13}-test-instrumentation-sqlalchemy-{1,2} py314-test-instrumentation-sqlalchemy-2 - pypy3-test-instrumentation-sqlalchemy-{0,1,2} + pypy3-test-instrumentation-sqlalchemy-{1,2} lint-instrumentation-sqlalchemy ; opentelemetry-instrumentation-redis - py3{9,10,11,12,13,14}-test-instrumentation-redis + py3{10,11,12,13,14}-test-instrumentation-redis pypy3-test-instrumentation-redis lint-instrumentation-redis ; opentelemetry-instrumentation-remoulade - py3{9,10,11,12,13,14}-test-instrumentation-remoulade + py3{10,11,12,13,14}-test-instrumentation-remoulade ; instrumentation-remoulade intentionally excluded from pypy3 lint-instrumentation-remoulade ; opentelemetry-instrumentation-celery - py3{9,10,11,12,13,14}-test-instrumentation-celery + py3{10,11,12,13,14}-test-instrumentation-celery pypy3-test-instrumentation-celery lint-instrumentation-celery ; opentelemetry-instrumentation-system-metrics - py3{9,10,11,12,13,14}-test-instrumentation-system-metrics + py3{10,11,12,13,14}-test-instrumentation-system-metrics pypy3-test-instrumentation-system-metrics lint-instrumentation-system-metrics ; opentelemetry-instrumentation-threading - py3{9,10,11,12,13,14}-test-instrumentation-threading + py3{10,11,12,13,14}-test-instrumentation-threading pypy3-test-instrumentation-threading lint-instrumentation-threading ; opentelemetry-instrumentation-tornado - py3{9,10,11,12,13,14}-test-instrumentation-tornado + py3{10,11,12,13,14}-test-instrumentation-tornado pypy3-test-instrumentation-tornado lint-instrumentation-tornado ; opentelemetry-instrumentation-tortoiseorm - py3{9,10,11,12,13,14}-test-instrumentation-tortoiseorm + py3{10,11,12,13,14}-test-instrumentation-tortoiseorm pypy3-test-instrumentation-tortoiseorm lint-instrumentation-tortoiseorm @@ -366,33 +358,33 @@ envlist = ; below mean these dependencies are being used: ; 0: httpx>=0.18.0,<0.19.0 respx~=0.17.0 ; 1: httpx>=0.19.0 respx~=0.20.1 - py3{9,10,11,12}-test-instrumentation-httpx-{0,1} - py3{13,14}-test-instrumentation-httpx-1 - pypy3-test-instrumentation-httpx-{0,1} + py3{10,11,12}-test-instrumentation-httpx-{0,1}-{wrapt1,wrapt2} + py3{13,14}-test-instrumentation-httpx-1-{wrapt1,wrapt2} + pypy3-test-instrumentation-httpx-{0,1}-wrapt2 lint-instrumentation-httpx ; opentelemetry-util-http - py3{9,10,11,12,13,14}-test-util-http + py3{10,11,12,13,14}-test-util-http pypy3-test-util-http lint-util-http ; opentelemetry-exporter-credential-provider-gcp - py3{9,10,11,12,13,14}-test-exporter-credential-provider-gcp + py3{10,11,12,13,14}-test-exporter-credential-provider-gcp lint-exporter-credential-provider-gcp ; opentelemetry-util-genai - py3{9,10,11,12,13,14}-test-util-genai + py3{10,11,12,13,14}-test-util-genai pypy3-test-util-genai lint-util-genai ; opentelemetry-propagator-aws-xray - py3{9,10,11,12,13,14}-test-propagator-aws-xray-{0,1} + py3{10,11,12,13,14}-test-propagator-aws-xray-{0,1} pypy3-test-propagator-aws-xray-{0,1} lint-propagator-aws-xray benchmark-propagator-aws-xray ; opentelemetry-propagator-ot-trace - py3{9,10,11,12,13,14}-test-propagator-ot-trace + py3{10,11,12,13,14}-test-propagator-ot-trace pypy3-test-propagator-ot-trace lint-propagator-ot-trace @@ -401,8 +393,8 @@ envlist = ; below mean these dependencies are being used: ; 0: pika>=0.12.0,<1.0.0 ; 1: pika>=1.0.0 - py3{9,10,11,12,13,14}-test-instrumentation-sio-pika-{0,1} - pypy3-test-instrumentation-sio-pika-{0,1} + py3{10,11,12,13,14}-test-instrumentation-sio-pika-{0,1}-{wrapt1,wrapt2} + pypy3-test-instrumentation-sio-pika-{0,1}-wrapt2 lint-instrumentation-sio-pika ; opentelemetry-instrumentation-aio-pika @@ -412,41 +404,47 @@ envlist = ; 1: aio_pika==8.3.0 ; 2: aio_pika==9.0.5 ; 3: aio_pika==9.4.1 - py3{9,10,11,12,13,14}-test-instrumentation-aio-pika-{0,1,2,3} + py3{10,11,12,13,14}-test-instrumentation-aio-pika-{0,1,2,3} pypy3-test-instrumentation-aio-pika-{0,1,2,3} lint-instrumentation-aio-pika ; opentelemetry-instrumentation-aiokafka - py3{9,10,11,12,13,14}-test-instrumentation-aiokafka + py3{10,11,12,13,14}-test-instrumentation-aiokafka pypy3-test-instrumentation-aiokafka lint-instrumentation-aiokafka ; opentelemetry-instrumentation-kafka-python - py3{9,10,11}-test-instrumentation-kafka-python - py3{9,10,11,12,13,14}-test-instrumentation-kafka-pythonng + py3{10,11}-test-instrumentation-kafka-python + py3{10,11,12,13,14}-test-instrumentation-kafka-pythonng pypy3-test-instrumentation-kafka-python pypy3-test-instrumentation-kafka-pythonng lint-instrumentation-kafka-python ; opentelemetry-instrumentation-confluent-kafka - py3{9,10,11,12,13,14}-test-instrumentation-confluent-kafka + py3{10,11,12,13,14}-test-instrumentation-confluent-kafka lint-instrumentation-confluent-kafka ; opentelemetry-instrumentation-asyncio - py3{9,10,11,12,13,14}-test-instrumentation-asyncio + py3{10,11,12,13,14}-test-instrumentation-asyncio lint-instrumentation-asyncio ; opentelemetry-instrumentation-cassandra - py3{9,10,11,12,13,14}-test-instrumentation-cassandra-{driver,scylla} + py3{10,11,12,13,14}-test-instrumentation-cassandra-{driver,scylla} pypy3-test-instrumentation-cassandra-scylla lint-instrumentation-cassandra ; opentelemetry-processor-baggage - py3{9,10,11,12,13,14}-test-processor-baggage + py3{10,11,12,13,14}-test-processor-baggage pypy3-test-processor-baggage ; requires snappy headers to be available on the system lint-processor-baggage + ; opentelemetry-opamp-client + py3{10,11,12,13,14}-test-opamp-client-{latest,lowest} + ; https://github.com/kevin1024/vcrpy/pull/775#issuecomment-1847849962 + ; pypy3-test-opamp-client + lint-opamp-client + spellcheck docker-tests docs @@ -458,17 +456,19 @@ envlist = [testenv] test_deps = - opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api - opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions - opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk - opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils + opentelemetry-api@{env:CORE_REPO_API} + opentelemetry-semantic-conventions@{env:CORE_REPO_SEMCONV} + opentelemetry-sdk@{env:CORE_REPO_SDK} + opentelemetry-test-utils@{env:CORE_REPO_TEST_UTILS} deps = lint: -r dev-requirements.txt coverage: pytest coverage: pytest-cov opentelemetry-instrumentation: {[testenv]test_deps} - opentelemetry-instrumentation: -r {toxinidir}/opentelemetry-instrumentation/test-requirements.txt + opentelemetry-instrumentation-wrapt1: -r {toxinidir}/opentelemetry-instrumentation/test-requirements-wrapt1.txt + opentelemetry-instrumentation-wrapt2: -r {toxinidir}/opentelemetry-instrumentation/test-requirements-wrapt2.txt + lint-opentelemetry-instrumentation: -r {toxinidir}/opentelemetry-instrumentation/test-requirements-wrapt2.txt distro: {[testenv]test_deps} distro: -r {toxinidir}/opentelemetry-distro/test-requirements.txt @@ -479,6 +479,8 @@ deps = # and the latest version of OTel API and SDK openai-latest: {[testenv]test_deps} openai-latest: -r {toxinidir}/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.latest.txt + openai-pydantic1: {[testenv]test_deps} + openai-pydantic1: -r {toxinidir}/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.pydantic1.txt lint-instrumentation-openai-v2: -r {toxinidir}/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt openai_agents-oldest: -r {toxinidir}/instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.oldest.txt openai_agents-latest: {[testenv]test_deps} @@ -509,7 +511,6 @@ deps = asgi: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi/test-requirements.txt celery: {[testenv]test_deps} - py3{9}-test-instrumentation-celery: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-0.txt py3{10,11,12,13,14}-test-instrumentation-celery: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt pypy3-test-instrumentation-celery: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt lint-instrumentation-celery: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-celery/test-requirements-1.txt @@ -521,9 +522,11 @@ deps = click: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-click/test-requirements.txt sio-pika: {[testenv]test_deps} - sio-pika-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0.txt - sio-pika-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt - lint-instrumentation-sio-pika: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1.txt + sio-pika-0-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt1.txt + sio-pika-0-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-0-wrapt2.txt + sio-pika-1-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt1.txt + sio-pika-1-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt2.txt + lint-instrumentation-sio-pika: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pika/test-requirements-1-wrapt2.txt aio-pika: {[testenv]test_deps} aio-pika-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika/test-requirements-0.txt @@ -545,22 +548,23 @@ deps = confluent-kafka: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka/test-requirements.txt grpc: {[testenv]test_deps} - grpc-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0.txt - grpc-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1.txt - lint-instrumentation-grpc: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1.txt + grpc-0-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt1.txt + grpc-0-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-0-wrapt2.txt + grpc-1-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt1.txt + grpc-1-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt2.txt + lint-instrumentation-grpc: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc/test-requirements-1-wrapt2.txt wsgi: {[testenv]test_deps} wsgi: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi/test-requirements.txt asyncpg: {[testenv]test_deps} - asyncpg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements.txt + asyncpg-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt1.txt + asyncpg-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt2.txt + lint-instrumentation-asyncpg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg/test-requirements-wrapt2.txt aws-lambda: {[testenv]test_deps} aws-lambda: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda/test-requirements.txt - boto: {[testenv]test_deps} - boto: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-boto/test-requirements.txt - boto3sqs: {[testenv]test_deps} boto3sqs: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-boto3sqs/test-requirements.txt @@ -588,9 +592,14 @@ deps = lint-instrumentation-urllib3: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt botocore: {[testenv]test_deps} - botocore-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0.txt - botocore-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1.txt - lint-instrumentation-botocore: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1.txt + botocore-0-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt1.txt + botocore-0-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-0-wrapt2.txt + botocore-1-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt1.txt + botocore-1-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-1-wrapt2.txt + ; Note: aiobotocore is not compatible with wrapt2 + botocore-2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-2.txt + botocore-3: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-3.txt + lint-instrumentation-botocore: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/test-requirements-3.txt cassandra: {[testenv]test_deps} cassandra-driver: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt @@ -598,16 +607,15 @@ deps = lint-instrumentation-cassandra: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-cassandra/test-requirements.txt dbapi: {[testenv]test_deps} - dbapi: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements.txt + dbapi-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt1.txt + dbapi-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt2.txt + lint-instrumentation-dbapi: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi/test-requirements-wrapt2.txt django: {[testenv]test_deps} - py3{9}-test-instrumentation-django-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt - py3{9}-test-instrumentation-django-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt - py3{9}-test-instrumentation-django-2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt - py3{10,11,12}-test-instrumentation-django-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt - py3{10,11,12,13,14}-test-instrumentation-django-3: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt - pypy3-test-instrumentation-django-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt - pypy3-test-instrumentation-django-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt + django-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-0.txt + django-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-1.txt + django-2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-2.txt + django-3: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt lint-instrumentation-django: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-django/test-requirements-3.txt fastapi: {[testenv]test_deps} @@ -633,7 +641,6 @@ deps = pymongo: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo/test-requirements.txt psycopg: {[testenv]test_deps} - py3{9}-test-instrumentation-psycopg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-0.txt py3{10,11,12,13,14}-test-instrumentation-psycopg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt pypy3-test-instrumentation-psycopg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt lint-instrumentation-psycopg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg/test-requirements-1.txt @@ -695,7 +702,9 @@ deps = aiohttp-server: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-server/test-requirements.txt aiopg: {[testenv]test_deps} - aiopg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements.txt + aiopg-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt1.txt + aiopg-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt2.txt + lint-instrumentation-aiopg: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg/test-requirements-wrapt2.txt richconsole: {[testenv]test_deps} richconsole: -r {toxinidir}/exporter/opentelemetry-exporter-richconsole/test-requirements.txt @@ -719,9 +728,11 @@ deps = asyncio: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncio/test-requirements.txt httpx: {[testenv]test_deps} - httpx-0: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0.txt - httpx-1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt - lint-instrumentation-httpx: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1.txt + httpx-0-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt1.txt + httpx-0-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-0-wrapt2.txt + httpx-1-wrapt1: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt1.txt + httpx-1-wrapt2: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt2.txt + lint-instrumentation-httpx: -r {toxinidir}/instrumentation/opentelemetry-instrumentation-httpx/test-requirements-1-wrapt2.txt # packages that are released individually should provide a test-requirements.txt with the lowest version of OTel API # and SDK supported to test we are honoring it @@ -758,6 +769,11 @@ deps = processor-baggage: {[testenv]test_deps} processor-baggage: -r {toxinidir}/processor/opentelemetry-processor-baggage/test-requirements.txt + opamp-client: {[testenv]test_deps} + opamp-client-lowest: -r {toxinidir}/opamp/opentelemetry-opamp-client/test-requirements.lowest.txt + opamp-client-latest: -r {toxinidir}/opamp/opentelemetry-opamp-client/test-requirements.latest.txt + lint-opamp-client: -r {toxinidir}/opamp/opentelemetry-opamp-client/test-requirements.lowest.txt + util-http: {[testenv]test_deps} util-http: -r {toxinidir}/util/opentelemetry-util-http/test-requirements.txt util-http: {toxinidir}/util/opentelemetry-util-http @@ -779,6 +795,10 @@ setenv = ; i.e: CORE_REPO_SHA=dde62cebffe519c35875af6d06fae053b3be65ec tox -e CORE_REPO_SHA={env:CORE_REPO_SHA:main} CORE_REPO=git+https://github.com/open-telemetry/opentelemetry-python.git@{env:CORE_REPO_SHA} + CORE_REPO_API={env:CORE_REPO_API:{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api} + CORE_REPO_SDK={env:CORE_REPO_SDK:{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk} + CORE_REPO_SEMCONV={env:CORE_REPO_SEMCONV:{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions} + CORE_REPO_TEST_UTILS={env:CORE_REPO_TEST_UTILS:{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils} UV_CONFIG_FILE={toxinidir}/tox-uv.toml PIP_CONSTRAINTS={toxinidir}/test-constraints.txt UV_BUILD_CONSTRAINT={toxinidir}/test-constraints.txt @@ -816,9 +836,6 @@ commands = test-instrumentation-aws-lambda: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-aws-lambda/tests {posargs} lint-instrumentation-aws-lambda: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-aws-lambda" - test-instrumentation-boto: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-boto/tests {posargs} - lint-instrumentation-boto: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-boto" - test-instrumentation-botocore: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore/tests {posargs} lint-instrumentation-botocore: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-botocore" @@ -1007,6 +1024,9 @@ commands = test-exporter-prometheus-remote-write: pytest {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write/tests {posargs} lint-exporter-prometheus-remote-write: sh -c "cd exporter && pylint --rcfile ../.pylintrc opentelemetry-exporter-prometheus-remote-write" + test-opamp-client: pytest {toxinidir}/opamp/opentelemetry-opamp-client/tests {posargs} + lint-opamp-client: sh -c "cd opamp && pylint --rcfile ../.pylintrc opentelemetry-opamp-client" + coverage: {toxinidir}/scripts/coverage.sh [testenv:docs] @@ -1042,10 +1062,6 @@ deps = -e {toxinidir}/opentelemetry-instrumentation -e {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg -e {toxinidir}/instrumentation/opentelemetry-instrumentation-celery - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-pika - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aiokafka - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka -e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi -e {toxinidir}/instrumentation/opentelemetry-instrumentation-django -e {toxinidir}/instrumentation/opentelemetry-instrumentation-flask @@ -1059,10 +1075,8 @@ deps = -e {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg -e {toxinidir}/instrumentation/opentelemetry-instrumentation-redis - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade -e {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi -e {toxinidir}/util/opentelemetry-util-http - opentelemetry-exporter-opencensus@{env:CORE_REPO}\#egg=opentelemetry-exporter-opencensus&subdirectory=exporter/opentelemetry-exporter-opencensus changedir = tests/opentelemetry-docker-tests/tests @@ -1122,6 +1136,7 @@ commands = deps = -c {toxinidir}/dev-requirements.txt pyright + protobuf==6.31.1 {[testenv]test_deps} {toxinidir}/opentelemetry-instrumentation {toxinidir}/util/opentelemetry-util-http @@ -1135,6 +1150,7 @@ deps = {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncclick[instruments] {toxinidir}/exporter/opentelemetry-exporter-credential-provider-gcp {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client[instruments] + {toxinidir}/opamp/opentelemetry-opamp-client commands = pyright diff --git a/util/opentelemetry-util-genai/AGENTS.md b/util/opentelemetry-util-genai/AGENTS.md new file mode 100644 index 0000000000..355d8b7c08 --- /dev/null +++ b/util/opentelemetry-util-genai/AGENTS.md @@ -0,0 +1,80 @@ +# GenAI Utils — Agent and Contributor Guidelines + +This package provides shared telemetry utilities for OpenTelemetry GenAI instrumentation. + +## 1. Semantic Convention Compliance + +All attributes, operation names, and span names must match the +[OpenTelemetry GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/) +exactly. + +Use the appropriate semconv attribute modules — do not hardcode attribute name strings: + +- `gen_ai.*` attributes: `opentelemetry.semconv._incubating.attributes.gen_ai_attributes` +- `server.*` attributes: `opentelemetry.semconv.attributes.server_attributes` +- `error.*` attributes: `opentelemetry.semconv.attributes.error_attributes` +- Other namespaces: use the corresponding module from `opentelemetry.semconv` + +## 2. Invocation Lifecycle Pattern + +Every new operation type must follow this pattern: + +```python +invocation = handler.start_inference(provider, request_model, server_address=..., server_port=...) +invocation.temperature = ... +try: + response = client.call(...) + invocation.response_model_name = response.model + invocation.finish_reasons = response.finish_reasons + invocation.stop() +except Exception as exc: + invocation.fail(exc) + raise +``` + +Factory methods on `TelemetryHandler` (`handler.py`): + +- `start_inference(provider, request_model, *, server_address, server_port)` → `InferenceInvocation` +- `start_embedding(provider, request_model, *, server_address, server_port)` → `EmbeddingInvocation` +- `start_tool(name, *, arguments, tool_call_id, tool_type, tool_description)` → `ToolInvocation` +- `start_workflow(name)` → `WorkflowInvocation` + +Context manager equivalents (`handler.inference()`, `handler.embedding()`, `handler.tool()`, +`handler.workflow()`) are available when the span lifetime maps cleanly to a `with` block. + +### Anti-patterns + +**Never construct invocation types directly** (`InferenceInvocation(...)`, `ToolInvocation(...)`, +etc.) in instrumentation or production code — direct construction skips span creation and context +propagation, so all telemetry calls become no-ops. Always use `handler.start_*()`. + +## 3. Exception Handling + +- Do not add `raise {Error}` statements to `handler.py` or `types.py` — validation belongs in + tests and callers, not telemetry internals. +- When catching exceptions from the underlying library to record telemetry, always re-raise + the original exception unmodified. + +## 4. Documentation + +- Docstrings for invocation types and span/event helpers must include a link to the + corresponding operation in the semconv spec. +- When adding or changing attributes, update the docstring to describe what is set and under + what conditions (e.g., "set only when `server_address` is provided"). + +## 5. Tests + +- Every new operation type or attribute change must have tests verifying the exact attribute + names and values produced, checked against the semconv spec. +- Cover all paths: success (`invocation.stop()`), failure (`invocation.fail(exc)`), and any + conditional attribute logic (e.g., attributes set only when optional fields are populated). +- Tests live in `tests/` — follow existing patterns there. +- Don't call internal API in tests when the public API is available. + +## 6. Python API Conventions + +- Mark private modules with an underscore. +- Objects inside of a private module should be prefixed with underscopre if they + are not used outside the that module. +- Before removing or renaming an object exposed publicly, deprecate it first with + `@deprecated("... Use X instead.")` pointing to the replacement; diff --git a/util/opentelemetry-util-genai/CHANGELOG.md b/util/opentelemetry-util-genai/CHANGELOG.md index f64092a697..1ddd22cee8 100644 --- a/util/opentelemetry-util-genai/CHANGELOG.md +++ b/util/opentelemetry-util-genai/CHANGELOG.md @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Add metrics support for EmbeddingInvocation + ([#4377](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4377)) +- Add support for workflow in genAI utils handler. + ([#4366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4366)) +- Enrich ToolCall type, breaking change: usage of ToolCall class renamed to ToolCallRequest + ([#4218](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4218)) +- Add EmbeddingInvocation span lifecycle support + ([#4219](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4219)) +- Populate schema_url on metrics + ([#4320](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4320)) +- Add workflow invocation type to genAI utils + ([#4310](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4310)) +- Check if upload works at startup in initializer of the `UploadCompletionHook`, instead +of repeatedly failing on every upload ([#4390](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4390)). +- Refactor public API: add factory methods (`start_inference`, `start_embedding`, `start_tool`, `start_workflow`) and invocation-owned lifecycle (`invocation.stop()` / `invocation.fail(exc)`); rename `LLMInvocation` → `InferenceInvocation` and `ToolCall` → `ToolInvocation`. Existing usages remain fully functional via deprecated aliases. + ([#4391](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4391)) + + ## Version 0.3b0 (2026-02-20) - Add `gen_ai.tool_definitions` to completion hook ([#4181](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4181)) @@ -16,29 +34,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Log error when `fsspec` fails to be imported instead of silently failing ([#4037](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4037)). - Minor change to check LRU cache in Completion Hook before acquiring semaphore/thread ([#3907](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3907)). - Add environment variable for genai upload hook queue size - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3943](#3943)) + ([#3943](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3943)) - Add more Semconv attributes to LLMInvocation spans. - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3862](#3862)) + ([#3862](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3862)) - Limit the upload hook thread pool to 64 workers - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3944](#3944)) + ([#3944](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3944)) - Add metrics to LLMInvocation traces - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3891](#3891)) + ([#3891](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3891)) - Add parent class genAI invocation - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3889](#3889)) + ([#3889](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3889)) ## Version 0.2b0 (2025-10-14) - Add jsonlines support to fsspec uploader - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3791](#3791)) + ([#3791](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3791)) - Rename "fsspec_upload" entry point and classes to more generic "upload" - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3798](#3798)) + ([#3798](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3798)) - Record content-type and use canonical paths in fsspec genai uploader - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3795](#3795)) + ([#3795](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3795)) - Make inputs / outputs / system instructions optional params to `on_completion`, - ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3802](#3802)). - - Use a SHA256 hash of the system instructions as it's upload filename, and check - if the file exists before re-uploading it, ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3814](#3814)). - + ([#3802](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3802)). +- Use a SHA256 hash of the system instructions as it's upload filename, and check + if the file exists before re-uploading it, ([#3814](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3814)). ## Version 0.1b0 (2025-09-25) @@ -52,6 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3752](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3752)) ([#3759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3759)) ([#3763](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3763)) + - Add a utility to parse the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable. Add `gen_ai_latest_experimental` as a new value to the Sem Conv stability flag ([#3716](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3716)). diff --git a/util/opentelemetry-util-genai/CLAUDE.md b/util/opentelemetry-util-genai/CLAUDE.md new file mode 120000 index 0000000000..47dc3e3d86 --- /dev/null +++ b/util/opentelemetry-util-genai/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/util/opentelemetry-util-genai/README.rst b/util/opentelemetry-util-genai/README.rst index bc4b348fcf..1394b8160a 100644 --- a/util/opentelemetry-util-genai/README.rst +++ b/util/opentelemetry-util-genai/README.rst @@ -1,46 +1,129 @@ OpenTelemetry Util for GenAI ============================ +The GenAI Utils package provides boilerplate and helpers to standardize instrumentation for Generative AI. +It offers APIs to minimize the work needed to instrument GenAI libraries, +while providing standardization for generating spans, metrics, and events. -The GenAI Utils package will include boilerplate and helpers to standardize instrumentation for Generative AI. -This package will provide APIs and decorators to minimize the work needed to instrument genai libraries, -while providing standardization for generating both types of otel, "spans and metrics" and "spans, metrics and events" -This package relies on environment variables to configure capturing of message content. +Key Components +-------------- + +- ``TelemetryHandler`` -- manages LLM invocation lifecycles (spans, metrics, events) +- ``LLMInvocation`` and message types (``Text``, ``Reasoning``, ``Blob``, etc.) -- structured data model for GenAI interactions +- ``CompletionHook`` -- protocol for uploading content to external storage (built-in ``fsspec`` support) +- Metrics -- ``gen_ai.client.operation.duration`` and ``gen_ai.client.token.usage`` histograms + + +Usage +----- + +See the module docstring in ``opentelemetry.util.genai.handler`` for usage examples, +including context manager and manual lifecycle patterns. + + +Environment Variables +--------------------- + +This package relies on environment variables to configure capturing of message content. By default, message content will not be captured. -Set the environment variable `OTEL_SEMCONV_STABILITY_OPT_IN` to `gen_ai_latest_experimental` to enable experimental features. -Set the environment variable `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` to one of: -- `NO_CONTENT`: Do not capture message content (default). -- `SPAN_ONLY`: Capture message content in spans only. -- `EVENT_ONLY`: Capture message content in events only. -- `SPAN_AND_EVENT`: Capture message content in both spans and events. - -To control event emission, you can optionally set `OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT` to `true` or `false` (case-insensitive). -This variable controls whether to emit `gen_ai.client.inference.operation.details` events. -If not explicitly set, the default value is automatically determined by `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`: -- When `NO_CONTENT` or `SPAN_ONLY` is set: defaults to `false` -- When `EVENT_ONLY` or `SPAN_AND_EVENT` is set: defaults to `true` +Set the environment variable ``OTEL_SEMCONV_STABILITY_OPT_IN`` to ``gen_ai_latest_experimental`` to enable experimental features. +Set the environment variable ``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`` to one of: + +- ``NO_CONTENT``: Do not capture message content (default). +- ``SPAN_ONLY``: Capture message content in spans only. +- ``EVENT_ONLY``: Capture message content in events only. +- ``SPAN_AND_EVENT``: Capture message content in both spans and events. + +To control event emission, you can optionally set ``OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT`` to ``true`` or ``false`` (case-insensitive). +This variable controls whether to emit ``gen_ai.client.inference.operation.details`` events. +If not explicitly set, the default value is automatically determined by ``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT``: + +- When ``NO_CONTENT`` or ``SPAN_ONLY`` is set: defaults to ``false`` +- When ``EVENT_ONLY`` or ``SPAN_AND_EVENT`` is set: defaults to ``true`` + If explicitly set, the user's value takes precedence over the default. -This package provides these span attributes: +When ``EVENT_ONLY`` or ``SPAN_AND_EVENT`` mode is enabled and a LoggerProvider is configured, +the package also emits ``gen_ai.client.inference.operation.details`` events with structured +message content (as dictionaries instead of JSON strings). Note that when using ``EVENT_ONLY`` +or ``SPAN_AND_EVENT``, the ``OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT`` environment variable defaults +to ``true``, so events will be emitted automatically unless explicitly set to ``false``. + +Completion Hook / Upload +^^^^^^^^^^^^^^^^^^^^^^^^ + +- ``OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK``: Name of the completion hook entry point to load (e.g. ``upload``). +- ``OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH``: An ``fsspec``-compatible URI/path for uploading prompts and responses + (e.g. ``/path/to/prompts`` or ``gs://my_bucket``). Required when using the ``upload`` hook. +- ``OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT``: Format for uploaded data -- ``json`` (default) or ``jsonl``. +- ``OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE``: Maximum number of concurrent uploads to queue (default: ``20``). + + +Span Attributes +--------------- + +This package sets the following span attributes on LLM invocations: + +**Common attributes:** -- `gen_ai.provider.name`: Str(openai) -- `gen_ai.operation.name`: Str(chat) -- `gen_ai.request.model`: Str(gpt-3.5-turbo) -- `gen_ai.response.finish_reasons`: Slice(["stop"]) -- `gen_ai.response.model`: Str(gpt-3.5-turbo-0125) -- `gen_ai.response.id`: Str(chatcmpl-Bz8yrvPnydD9pObv625n2CGBPHS13) -- `gen_ai.usage.input_tokens`: Int(24) -- `gen_ai.usage.output_tokens`: Int(7) -- `gen_ai.input.messages`: Str('[{"role": "Human", "parts": [{"content": "hello world", "type": "text"}]}]') -- `gen_ai.output.messages`: Str('[{"role": "AI", "parts": [{"content": "hello back", "type": "text"}], "finish_reason": "stop"}]') -- `gen_ai.system_instructions`: Str('[{"content": "You are a helpful assistant.", "type": "text"}]') (when system instruction is provided) +- ``gen_ai.operation.name``: Str(chat) +- ``gen_ai.provider.name``: Str(openai) +- ``gen_ai.request.model``: Str(gpt-4o) +- ``server.address``: Str(api.openai.com) +- ``server.port``: Int(443) -When `EVENT_ONLY` or `SPAN_AND_EVENT` mode is enabled and a LoggerProvider is configured, -the package also emits `gen_ai.client.inference.operation.details` events with structured -message content (as dictionaries instead of JSON strings). Note that when using `EVENT_ONLY` -or `SPAN_AND_EVENT`, the `OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT` environment variable defaults -to `true`, so events will be emitted automatically unless explicitly set to `false`. +**Response attributes:** + +- ``gen_ai.response.finish_reasons``: Slice(["stop"]) +- ``gen_ai.response.model``: Str(gpt-4o-2024-05-13) +- ``gen_ai.response.id``: Str(chatcmpl-Bz8yrvPnydD9pObv625n2CGBPHS13) +- ``gen_ai.usage.input_tokens``: Int(24) +- ``gen_ai.usage.output_tokens``: Int(7) + +**Request parameter attributes (when provided):** + +- ``gen_ai.request.temperature``: Float(0.7) +- ``gen_ai.request.top_p``: Float(1.0) +- ``gen_ai.request.frequency_penalty``: Float(0.0) +- ``gen_ai.request.presence_penalty``: Float(0.0) +- ``gen_ai.request.max_tokens``: Int(1024) +- ``gen_ai.request.stop_sequences``: Slice(["\\n"]) +- ``gen_ai.request.seed``: Int(42) + +**Content attributes (sensitive, requires content capturing enabled):** + +- ``gen_ai.input.messages``: Str('[{"role": "user", "parts": [{"content": "hello world", "type": "text"}]}]') +- ``gen_ai.output.messages``: Str('[{"role": "assistant", "parts": [{"content": "hello back", "type": "text"}], "finish_reason": "stop"}]') +- ``gen_ai.system_instructions``: Str('[{"content": "You are a helpful assistant.", "type": "text"}]') + +**Error attributes:** + +- ``error.type``: Str(TimeoutError) + +Embedding Span Attributes +------------------------- + +This package also supports embedding invocation spans via the ``embedding`` context manager. +For embedding invocations, the following attributes are set: + +**Common attributes:** + +- ``gen_ai.operation.name``: Str(embeddings) +- ``gen_ai.provider.name``: Str(openai) +- ``server.address``: Str(api.openai.com) +- ``server.port``: Int(443) + +**Request attributes:** + +- ``gen_ai.request.model``: Str(text-embedding-3-small) +- ``gen_ai.embeddings.dimension.count``: Int(1536) +- ``gen_ai.request.encoding_formats``: Slice(["float"]) + +**Response attributes:** + +- ``gen_ai.response.model``: Str(text-embedding-3-small) +- ``gen_ai.usage.input_tokens``: Int(24) Installation @@ -50,11 +133,15 @@ Installation pip install opentelemetry-util-genai +For upload support (requires ``fsspec``):: + + pip install opentelemetry-util-genai[upload] + Design Document --------------- -The design document for the OpenTelemetry GenAI Utils can be found at: `Design Document `_ +The design document for the OpenTelemetry GenAI Utils can be found at: `Design Document `_ References ---------- diff --git a/util/opentelemetry-util-genai/pyproject.toml b/util/opentelemetry-util-genai/pyproject.toml index c9d4d388c1..3e95fb3039 100644 --- a/util/opentelemetry-util-genai/pyproject.toml +++ b/util/opentelemetry-util-genai/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "OpenTelemetry GenAI Utils" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -26,9 +25,9 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "opentelemetry-instrumentation ~= 0.58b0", - "opentelemetry-semantic-conventions ~= 0.58b0", - "opentelemetry-api>=1.31.0", + "opentelemetry-instrumentation ~= 0.60b0", + "opentelemetry-semantic-conventions ~= 0.60b0", + "opentelemetry-api>=1.39", ] [project.entry-points.opentelemetry_genai_completion_hook] @@ -46,10 +45,8 @@ Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib" path = "src/opentelemetry/util/genai/version.py" [tool.hatch.build.targets.sdist] -include = [ - "/src", - "/tests", -] +include = ["/src", "/tests"] [tool.hatch.build.targets.wheel] packages = ["src/opentelemetry"] + diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_embedding_invocation.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_embedding_invocation.py new file mode 100644 index 0000000000..c4a2f2f427 --- /dev/null +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_embedding_invocation.py @@ -0,0 +1,123 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import Any + +from opentelemetry._logs import Logger +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAI, +) +from opentelemetry.semconv.attributes import server_attributes +from opentelemetry.trace import SpanKind, Tracer +from opentelemetry.util.genai._invocation import Error, GenAIInvocation +from opentelemetry.util.genai.metrics import InvocationMetricsRecorder +from opentelemetry.util.types import AttributeValue + + +class EmbeddingInvocation(GenAIInvocation): + """Represents a single embedding model invocation. + + Use handler.start_embedding(provider) or the handler.embedding(provider) + context manager rather than constructing this directly. + """ + + def __init__( + self, + tracer: Tracer, + metrics_recorder: InvocationMetricsRecorder, + logger: Logger, + provider: str, + *, + request_model: str | None = None, + server_address: str | None = None, + server_port: int | None = None, + encoding_formats: list[str] | None = None, + input_tokens: int | None = None, + dimension_count: int | None = None, + response_model_name: str | None = None, + attributes: dict[str, Any] | None = None, + metric_attributes: dict[str, Any] | None = None, + ) -> None: + """Use handler.start_embedding(provider) or handler.embedding(provider) instead of calling this directly.""" + _operation_name = GenAI.GenAiOperationNameValues.EMBEDDINGS.value + super().__init__( + tracer, + metrics_recorder, + logger, + operation_name=_operation_name, + span_name=f"{_operation_name} {request_model}" + if request_model + else _operation_name, + span_kind=SpanKind.CLIENT, + attributes=attributes, + metric_attributes=metric_attributes, + ) + self.provider = provider # e.g., azure.ai.openai, openai, aws.bedrock + self.request_model = request_model + self.server_address = server_address + self.server_port = server_port + # encoding_formats can be multi-value -> combinational cardinality risk. + # Keep on spans/events only. + self.encoding_formats = encoding_formats + self.input_tokens = input_tokens + self.dimension_count = dimension_count + self.response_model_name = response_model_name + self._start() + + def _get_metric_attributes(self) -> dict[str, Any]: + optional_attrs = ( + (GenAI.GEN_AI_PROVIDER_NAME, self.provider), + (GenAI.GEN_AI_REQUEST_MODEL, self.request_model), + (GenAI.GEN_AI_RESPONSE_MODEL, self.response_model_name), + (server_attributes.SERVER_ADDRESS, self.server_address), + (server_attributes.SERVER_PORT, self.server_port), + ) + attrs: dict[str, AttributeValue] = { + GenAI.GEN_AI_OPERATION_NAME: self._operation_name, + **{k: v for k, v in optional_attrs if v is not None}, + } + attrs.update(self.metric_attributes) + return attrs + + def _get_metric_token_counts(self) -> dict[str, int]: + if self.input_tokens is not None: + return {GenAI.GenAiTokenTypeValues.INPUT.value: self.input_tokens} + return {} + + def _apply_finish(self, error: Error | None = None) -> None: + optional_attrs = ( + (GenAI.GEN_AI_PROVIDER_NAME, self.provider), + (server_attributes.SERVER_ADDRESS, self.server_address), + (server_attributes.SERVER_PORT, self.server_port), + (GenAI.GEN_AI_REQUEST_MODEL, self.request_model), + (GenAI.GEN_AI_EMBEDDINGS_DIMENSION_COUNT, self.dimension_count), + (GenAI.GEN_AI_REQUEST_ENCODING_FORMATS, self.encoding_formats), + (GenAI.GEN_AI_RESPONSE_MODEL, self.response_model_name), + (GenAI.GEN_AI_USAGE_INPUT_TOKENS, self.input_tokens), + ) + attributes: dict[str, Any] = { + GenAI.GEN_AI_OPERATION_NAME: self._operation_name, + **{ + key: value + for key, value in optional_attrs + if value is not None + }, + } + if error is not None: + self._apply_error_attributes(error) + attributes.update(self.attributes) + self.span.set_attributes(attributes) + self._metrics_recorder.record(self) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py new file mode 100644 index 0000000000..a1bd55811c --- /dev/null +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py @@ -0,0 +1,358 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from dataclasses import asdict, dataclass, field +from typing import Any + +from opentelemetry._logs import Logger, LogRecord +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAI, +) +from opentelemetry.semconv.attributes import server_attributes +from opentelemetry.trace import INVALID_SPAN, Span, SpanKind, Tracer +from opentelemetry.util.genai._invocation import Error, GenAIInvocation +from opentelemetry.util.genai.metrics import InvocationMetricsRecorder +from opentelemetry.util.genai.types import ( + InputMessage, + MessagePart, + OutputMessage, +) +from opentelemetry.util.genai.utils import ( + ContentCapturingMode, + gen_ai_json_dumps, + get_content_capturing_mode, + is_experimental_mode, + should_emit_event, +) + + +class InferenceInvocation(GenAIInvocation): + """Represents a single LLM chat/completion call. + + Use handler.start_inference(provider) or the handler.inference(provider) + context manager rather than constructing this directly. + """ + + def __init__( # pylint: disable=too-many-locals + self, + tracer: Tracer, + metrics_recorder: InvocationMetricsRecorder, + logger: Logger, + provider: str, + *, + request_model: str | None = None, + input_messages: list[InputMessage] | None = None, + output_messages: list[OutputMessage] | None = None, + system_instruction: list[MessagePart] | None = None, + response_model_name: str | None = None, + response_id: str | None = None, + finish_reasons: list[str] | None = None, + input_tokens: int | None = None, + output_tokens: int | None = None, + temperature: float | None = None, + top_p: float | None = None, + frequency_penalty: float | None = None, + presence_penalty: float | None = None, + max_tokens: int | None = None, + stop_sequences: list[str] | None = None, + seed: int | None = None, + server_address: str | None = None, + server_port: int | None = None, + attributes: dict[str, Any] | None = None, + metric_attributes: dict[str, Any] | None = None, + ) -> None: + """Use handler.start_inference(provider) or handler.inference(provider) instead of calling this directly.""" + _operation_name = GenAI.GenAiOperationNameValues.CHAT.value + super().__init__( + tracer, + metrics_recorder, + logger, + operation_name=_operation_name, + span_name=f"{_operation_name} {request_model}" + if request_model + else _operation_name, + span_kind=SpanKind.CLIENT, + attributes=attributes, + metric_attributes=metric_attributes, + ) + self.provider = provider + self.request_model = request_model + self.input_messages: list[InputMessage] = ( + [] if input_messages is None else input_messages + ) + self.output_messages: list[OutputMessage] = ( + [] if output_messages is None else output_messages + ) + self.system_instruction: list[MessagePart] = ( + [] if system_instruction is None else system_instruction + ) + self.response_model_name = response_model_name + self.response_id = response_id + self.finish_reasons = finish_reasons + self.input_tokens = input_tokens + self.output_tokens = output_tokens + self.temperature = temperature + self.top_p = top_p + self.frequency_penalty = frequency_penalty + self.presence_penalty = presence_penalty + self.max_tokens = max_tokens + self.stop_sequences = stop_sequences + self.seed = seed + self.server_address = server_address + self.server_port = server_port + self._start() + + def _get_message_attributes(self, *, for_span: bool) -> dict[str, Any]: + if not is_experimental_mode(): + return {} + mode = get_content_capturing_mode() + allowed_modes = ( + ( + ContentCapturingMode.SPAN_ONLY, + ContentCapturingMode.SPAN_AND_EVENT, + ) + if for_span + else ( + ContentCapturingMode.EVENT_ONLY, + ContentCapturingMode.SPAN_AND_EVENT, + ) + ) + if mode not in allowed_modes: + return {} + + def serialize(items: list[Any]) -> Any: + dicts = [asdict(item) for item in items] + return gen_ai_json_dumps(dicts) if for_span else dicts + + optional_attrs = ( + ( + GenAI.GEN_AI_INPUT_MESSAGES, + serialize(self.input_messages) + if self.input_messages + else None, + ), + ( + GenAI.GEN_AI_OUTPUT_MESSAGES, + serialize(self.output_messages) + if self.output_messages + else None, + ), + ( + GenAI.GEN_AI_SYSTEM_INSTRUCTIONS, + serialize(self.system_instruction) + if self.system_instruction + else None, + ), + ) + return { + key: value for key, value in optional_attrs if value is not None + } + + def _get_finish_reasons(self) -> list[str] | None: + if self.finish_reasons is not None: + return self.finish_reasons or None + if self.output_messages: + reasons = [ + msg.finish_reason + for msg in self.output_messages + if msg.finish_reason + ] + return reasons or None + return None + + def _get_base_attributes(self) -> dict[str, Any]: + optional_attrs = ( + (GenAI.GEN_AI_REQUEST_MODEL, self.request_model), + (GenAI.GEN_AI_PROVIDER_NAME, self.provider), + (server_attributes.SERVER_ADDRESS, self.server_address), + (server_attributes.SERVER_PORT, self.server_port), + ) + return { + GenAI.GEN_AI_OPERATION_NAME: self._operation_name, + **{k: v for k, v in optional_attrs if v is not None}, + } + + def _get_attributes(self) -> dict[str, Any]: + attrs = self._get_base_attributes() + optional_attrs = ( + (GenAI.GEN_AI_REQUEST_TEMPERATURE, self.temperature), + (GenAI.GEN_AI_REQUEST_TOP_P, self.top_p), + (GenAI.GEN_AI_REQUEST_FREQUENCY_PENALTY, self.frequency_penalty), + (GenAI.GEN_AI_REQUEST_PRESENCE_PENALTY, self.presence_penalty), + (GenAI.GEN_AI_REQUEST_MAX_TOKENS, self.max_tokens), + (GenAI.GEN_AI_REQUEST_STOP_SEQUENCES, self.stop_sequences), + (GenAI.GEN_AI_REQUEST_SEED, self.seed), + (GenAI.GEN_AI_RESPONSE_FINISH_REASONS, self._get_finish_reasons()), + (GenAI.GEN_AI_RESPONSE_MODEL, self.response_model_name), + (GenAI.GEN_AI_RESPONSE_ID, self.response_id), + (GenAI.GEN_AI_USAGE_INPUT_TOKENS, self.input_tokens), + (GenAI.GEN_AI_USAGE_OUTPUT_TOKENS, self.output_tokens), + ) + attrs.update({k: v for k, v in optional_attrs if v is not None}) + return attrs + + def _get_metric_attributes(self) -> dict[str, Any]: + attrs = self._get_base_attributes() + if self.response_model_name is not None: + attrs[GenAI.GEN_AI_RESPONSE_MODEL] = self.response_model_name + attrs.update(self.metric_attributes) + return attrs + + def _get_metric_token_counts(self) -> dict[str, int]: + counts: dict[str, int] = {} + if self.input_tokens is not None: + counts[GenAI.GenAiTokenTypeValues.INPUT.value] = self.input_tokens + if self.output_tokens is not None: + counts[GenAI.GenAiTokenTypeValues.OUTPUT.value] = ( + self.output_tokens + ) + return counts + + def _apply_finish(self, error: Error | None = None) -> None: + if error is not None: + self._apply_error_attributes(error) + attributes = self._get_attributes() + attributes.update(self._get_message_attributes(for_span=True)) + attributes.update(self.attributes) + self.span.set_attributes(attributes) + self._metrics_recorder.record(self) + self._emit_event() + + def _emit_event(self) -> None: + """Emit a gen_ai.client.inference.operation.details event. + + For more details, see the semantic convention documentation: + https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-events.md#event-eventgen_aiclientinferenceoperationdetails + """ + if not is_experimental_mode() or not should_emit_event(): + return + + attributes = self._get_attributes() + attributes.update(self._get_message_attributes(for_span=False)) + attributes.update(self.attributes) + self._logger.emit( + LogRecord( + event_name="gen_ai.client.inference.operation.details", + attributes=attributes, + context=self._span_context, + ) + ) + + +@dataclass +class LLMInvocation: + """Deprecated. Use InferenceInvocation instead. + + Data container for an LLM invocation. Pass to handler.start_llm() to start + the span, then update fields and call handler.stop_llm() or handler.fail_llm(). + """ + + request_model: str | None = None + input_messages: list[InputMessage] = field(default_factory=list) # pyright: ignore[reportUnknownVariableType] + output_messages: list[OutputMessage] = field(default_factory=list) # pyright: ignore[reportUnknownVariableType] + system_instruction: list[MessagePart] = field(default_factory=list) # pyright: ignore[reportUnknownVariableType] + provider: str | None = None + response_model_name: str | None = None + response_id: str | None = None + finish_reasons: list[str] | None = None + input_tokens: int | None = None + output_tokens: int | None = None + attributes: dict[str, Any] = field(default_factory=dict) # pyright: ignore[reportUnknownVariableType] + """Additional attributes to set on spans and/or events. Not set on metrics.""" + metric_attributes: dict[str, Any] = field(default_factory=dict) # pyright: ignore[reportUnknownVariableType] + """Additional attributes to set on metrics. Must be low cardinality. Not set on spans or events.""" + temperature: float | None = None + top_p: float | None = None + frequency_penalty: float | None = None + presence_penalty: float | None = None + max_tokens: int | None = None + stop_sequences: list[str] | None = None + seed: int | None = None + server_address: str | None = None + server_port: int | None = None + + _inference_invocation: InferenceInvocation | None = field( + default=None, init=False, repr=False + ) + + def _start_with_handler( + self, + tracer: Tracer, + metrics_recorder: InvocationMetricsRecorder, + logger: Logger, + ) -> None: + """Create and start an InferenceInvocation from this data container. Called by handler.start_llm().""" + self._inference_invocation = InferenceInvocation( + tracer, + metrics_recorder, + logger, + self.provider or "", + request_model=self.request_model, + input_messages=self.input_messages, + output_messages=self.output_messages, + system_instruction=self.system_instruction, + response_model_name=self.response_model_name, + response_id=self.response_id, + finish_reasons=self.finish_reasons, + input_tokens=self.input_tokens, + output_tokens=self.output_tokens, + temperature=self.temperature, + top_p=self.top_p, + frequency_penalty=self.frequency_penalty, + presence_penalty=self.presence_penalty, + max_tokens=self.max_tokens, + stop_sequences=self.stop_sequences, + seed=self.seed, + server_address=self.server_address, + server_port=self.server_port, + attributes=self.attributes, + metric_attributes=self.metric_attributes, + ) + + def _sync_to_invocation(self) -> None: + inv = self._inference_invocation + if inv is None: + return + inv.provider = self.provider or "" + inv.request_model = self.request_model + inv.input_messages = self.input_messages + inv.output_messages = self.output_messages + inv.system_instruction = self.system_instruction + inv.response_model_name = self.response_model_name + inv.response_id = self.response_id + inv.finish_reasons = self.finish_reasons + inv.input_tokens = self.input_tokens + inv.output_tokens = self.output_tokens + inv.temperature = self.temperature + inv.top_p = self.top_p + inv.frequency_penalty = self.frequency_penalty + inv.presence_penalty = self.presence_penalty + inv.max_tokens = self.max_tokens + inv.stop_sequences = self.stop_sequences + inv.seed = self.seed + inv.server_address = self.server_address + inv.server_port = self.server_port + inv.attributes = self.attributes + inv.metric_attributes = self.metric_attributes + + @property + def span(self) -> Span: + """The underlying span, for back-compat with code that checks span.is_recording().""" + return ( + self._inference_invocation.span + if self._inference_invocation is not None + else INVALID_SPAN + ) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_invocation.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_invocation.py new file mode 100644 index 0000000000..b63d9a32b4 --- /dev/null +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_invocation.py @@ -0,0 +1,140 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import timeit +from abc import ABC, abstractmethod +from contextlib import contextmanager +from contextvars import Token +from typing import TYPE_CHECKING, Any, Iterator + +from typing_extensions import Self, TypeAlias + +from opentelemetry._logs import Logger +from opentelemetry.context import Context, attach, detach +from opentelemetry.semconv.attributes import error_attributes +from opentelemetry.trace import INVALID_SPAN as _INVALID_SPAN +from opentelemetry.trace import Span, SpanKind, Tracer, set_span_in_context +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.util.genai.types import Error + +if TYPE_CHECKING: + from opentelemetry.util.genai.metrics import InvocationMetricsRecorder + +ContextToken: TypeAlias = Token[Context] + + +class GenAIInvocation(ABC): + """ + Base class for all GenAI invocation types. Manages the lifecycle of a single + GenAI operation (LLM call, embedding, tool execution, workflow, etc.). + + Use the factory methods on TelemetryHandler (start_inference, start_embedding, + start_workflow, start_tool) rather than constructing invocations directly. + """ + + def __init__( + self, + # Individual components instead of TelemetryHandler to avoid a circular + # import between handler.py and the invocation modules. + tracer: Tracer, + metrics_recorder: InvocationMetricsRecorder, + logger: Logger, + operation_name: str, + span_name: str, + span_kind: SpanKind = SpanKind.CLIENT, + attributes: dict[str, Any] | None = None, + metric_attributes: dict[str, Any] | None = None, + ) -> None: + self._tracer = tracer + self._metrics_recorder = metrics_recorder + self._logger = logger + self._operation_name: str = operation_name + self.attributes: dict[str, Any] = ( + {} if attributes is None else attributes + ) + """Additional attributes to set on spans and/or events. Not set on metrics.""" + self.metric_attributes: dict[str, Any] = ( + {} if metric_attributes is None else metric_attributes + ) + """Additional attributes to set on metrics. Must be low cardinality. Not set on spans or events.""" + self.span: Span = _INVALID_SPAN + self._span_context: Context + self._span_name: str = span_name + self._span_kind: SpanKind = span_kind + self._context_token: ContextToken | None = None + self._monotonic_start_s: float | None = None + + def _start(self) -> None: + """Start the invocation span and attach it to the current context.""" + self.span = self._tracer.start_span( + name=self._span_name, + kind=self._span_kind, + ) + self._span_context = set_span_in_context(self.span) + self._monotonic_start_s = timeit.default_timer() + self._context_token = attach(self._span_context) + + def _get_metric_attributes(self) -> dict[str, Any]: + """Return low-cardinality attributes for metric recording.""" + return self.metric_attributes + + def _get_metric_token_counts(self) -> dict[str, int]: # pylint: disable=no-self-use + """Return {token_type: count} for token histogram recording.""" + return {} + + def _apply_error_attributes(self, error: Error) -> None: + """Apply error status and error.type attribute to the span, events, and metrics.""" + error_type = error.type.__qualname__ + self.span.set_status(Status(StatusCode.ERROR, error.message)) + self.attributes[error_attributes.ERROR_TYPE] = error_type + self.metric_attributes[error_attributes.ERROR_TYPE] = error_type + + @abstractmethod + def _apply_finish(self, error: Error | None = None) -> None: + """Apply finish telemetry (attributes, metrics, events).""" + + def _finish(self, error: Error | None = None) -> None: + """Apply finish telemetry and end the span.""" + if self._context_token is None: + return + try: + self._apply_finish(error) + finally: + try: + detach(self._context_token) + except Exception: # pylint: disable=broad-except + pass + self.span.end() + + def stop(self) -> None: + """Finalize the invocation successfully and end its span.""" + self._finish() + + def fail(self, error: Error | BaseException) -> None: + """Fail the invocation and end its span with error status.""" + if isinstance(error, BaseException): + error = Error(type=type(error), message=str(error)) + self._finish(error) + + @contextmanager + def _managed(self) -> Iterator[Self]: + """Context manager that calls stop() on success or fail() on exception.""" + try: + yield self + except Exception as exc: + self.fail(exc) + raise + self.stop() diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_tool_invocation.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_tool_invocation.py new file mode 100644 index 0000000000..1ebbd85bc6 --- /dev/null +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_tool_invocation.py @@ -0,0 +1,96 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import Any + +from opentelemetry._logs import Logger +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAI, +) +from opentelemetry.trace import Tracer +from opentelemetry.util.genai._invocation import Error, GenAIInvocation +from opentelemetry.util.genai.metrics import InvocationMetricsRecorder + + +class ToolInvocation(GenAIInvocation): + """Represents a tool call invocation for execute_tool span tracking. + + Not used as a message part — use ToolCallRequest for that purpose. + + Use handler.start_tool(name) rather than constructing this directly. + + Reference: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-spans.md#execute-tool-span + + Semantic convention attributes for execute_tool spans: + - gen_ai.operation.name: "execute_tool" (Required) + - gen_ai.tool.name: Name of the tool (Recommended) + - gen_ai.tool.call.id: Tool call identifier (Recommended if available) + - gen_ai.tool.type: Type classification - "function", "extension", or "datastore" (Recommended if available) + - gen_ai.tool.description: Tool description (Recommended if available) + - gen_ai.tool.call.arguments: Parameters passed to tool (Opt-In, may contain sensitive data) + - gen_ai.tool.call.result: Result returned by tool (Opt-In, may contain sensitive data) + - error.type: Error type if operation failed (Conditionally Required) + """ + + def __init__( + self, + tracer: Tracer, + metrics_recorder: InvocationMetricsRecorder, + logger: Logger, + name: str, + *, + arguments: Any = None, + tool_call_id: str | None = None, + tool_type: str | None = None, + tool_description: str | None = None, + tool_result: Any = None, + attributes: dict[str, Any] | None = None, + metric_attributes: dict[str, Any] | None = None, + ) -> None: + """Use handler.start_tool(name) or handler.tool(name) instead of calling this directly.""" + _operation_name = GenAI.GenAiOperationNameValues.EXECUTE_TOOL.value + super().__init__( + tracer, + metrics_recorder, + logger, + operation_name=_operation_name, + span_name=f"{_operation_name} {name}" if name else _operation_name, + attributes=attributes, + metric_attributes=metric_attributes, + ) + self.name = name + self.arguments = arguments + self.tool_call_id = tool_call_id + self.tool_type = tool_type + self.tool_description = tool_description + self.tool_result = tool_result + self._start() + + def _apply_finish(self, error: Error | None = None) -> None: + if error is not None: + self._apply_error_attributes(error) + optional_attrs = ( + (GenAI.GEN_AI_TOOL_NAME, self.name), + (GenAI.GEN_AI_TOOL_CALL_ID, self.tool_call_id), + (GenAI.GEN_AI_TOOL_TYPE, self.tool_type), + (GenAI.GEN_AI_TOOL_DESCRIPTION, self.tool_description), + ) + attributes: dict[str, Any] = { + GenAI.GEN_AI_OPERATION_NAME: self._operation_name, + **{k: v for k, v in optional_attrs if v is not None}, + } + attributes.update(self.attributes) + self.span.set_attributes(attributes) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index 4f22861396..f2b9254f49 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -22,7 +22,7 @@ import posixpath import threading from collections import OrderedDict -from concurrent.futures import ( # pylint: disable=no-name-in-module; TODO #4199 +from concurrent.futures import ( Future, ThreadPoolExecutor, ) @@ -156,6 +156,30 @@ def __init__( f"Invalid {upload_format=}. Must be one of {_FORMATS}" ) self._format = upload_format + self._content_type = ( + "application/json" + if self._format == "json" + else "application/jsonl" + ) + test_path = posixpath.join( + self._base_path, + f".one_off_test_to_see_if_upload_works.{self._format}", + ) + try: + with self._fs.open( + test_path, "w", content_type=self._content_type + ) as file: + file.write("\n") + except Exception as exception: # pylint: disable=broad-exception-caught + raise ValueError( + f"Failed to write file to the following path, upload is not working: {test_path}.\n Got error: {exception}" + ) + # Try to delete the file.. But we don't explicitly ask people to grant the GCS delete IAM permission in our + # docs, so if delete fails just leave the file.. + try: + self._fs.rm_file(test_path) # pyright: ignore[reportUnknownMemberType] + except Exception: # pylint: disable=broad-exception-caught + pass # Use a ThreadPoolExecutor for its queueing and thread management. The semaphore # limits the number of queued tasks. If the queue is full, data will be dropped. @@ -271,13 +295,7 @@ def _do_upload( for message_idx, line in enumerate(message_lines): line[_MESSAGE_INDEX_KEY] = message_idx - content_type = ( - "application/json" - if self._format == "json" - else "application/jsonl" - ) - - with self._fs.open(path, "w", content_type=content_type) as file: + with self._fs.open(path, "w", content_type=self._content_type) as file: for message in message_lines: gen_ai_json_dump(message, file) file.write("\n") diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_workflow_invocation.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_workflow_invocation.py new file mode 100644 index 0000000000..e3b45535d2 --- /dev/null +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_workflow_invocation.py @@ -0,0 +1,115 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from dataclasses import asdict +from typing import Any + +from opentelemetry._logs import Logger +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAI, +) +from opentelemetry.trace import SpanKind, Tracer +from opentelemetry.util.genai._invocation import Error, GenAIInvocation +from opentelemetry.util.genai.metrics import InvocationMetricsRecorder +from opentelemetry.util.genai.types import ( + InputMessage, + OutputMessage, +) +from opentelemetry.util.genai.utils import ( + ContentCapturingMode, + gen_ai_json_dumps, + get_content_capturing_mode, + is_experimental_mode, +) + + +class WorkflowInvocation(GenAIInvocation): + """ + Represents a predetermined sequence of operations (e.g. agent, LLM, tool, + and retrieval invocations). A workflow groups multiple operations together, + accepting input(s) and producing final output(s). + + Use handler.start_workflow(name) or the handler.workflow(name) context + manager rather than constructing this directly. + """ + + def __init__( + self, + tracer: Tracer, + metrics_recorder: InvocationMetricsRecorder, + logger: Logger, + name: str | None, + *, + input_messages: list[InputMessage] | None = None, + output_messages: list[OutputMessage] | None = None, + attributes: dict[str, Any] | None = None, + metric_attributes: dict[str, Any] | None = None, + ) -> None: + """Use handler.start_workflow(name) or handler.workflow(name) instead of calling this directly.""" + _operation_name = "invoke_workflow" + super().__init__( + tracer, + metrics_recorder, + logger, + operation_name=_operation_name, + span_name=f"{_operation_name} {name}" if name else _operation_name, + span_kind=SpanKind.INTERNAL, + attributes=attributes, + metric_attributes=metric_attributes, + ) + self.name = name + self.input_messages: list[InputMessage] = ( + [] if input_messages is None else input_messages + ) + self.output_messages: list[OutputMessage] = ( + [] if output_messages is None else output_messages + ) + self._start() + + def _get_messages_for_span(self) -> dict[str, Any]: + if not is_experimental_mode() or get_content_capturing_mode() not in ( + ContentCapturingMode.SPAN_ONLY, + ContentCapturingMode.SPAN_AND_EVENT, + ): + return {} + optional_attrs = ( + ( + GenAI.GEN_AI_INPUT_MESSAGES, + gen_ai_json_dumps([asdict(m) for m in self.input_messages]) + if self.input_messages + else None, + ), + ( + GenAI.GEN_AI_OUTPUT_MESSAGES, + gen_ai_json_dumps([asdict(m) for m in self.output_messages]) + if self.output_messages + else None, + ), + ) + return { + key: value for key, value in optional_attrs if value is not None + } + + def _apply_finish(self, error: Error | None = None) -> None: + attributes: dict[str, Any] = { + GenAI.GEN_AI_OPERATION_NAME: self._operation_name + } + attributes.update(self._get_messages_for_span()) + if error is not None: + self._apply_error_attributes(error) + attributes.update(self.attributes) + self.span.set_attributes(attributes) + # TODO: Add workflow metrics when supported diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py index 54e626deaa..9ef4a5592d 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py @@ -17,7 +17,6 @@ This module exposes the `TelemetryHandler` class, which manages the lifecycle of GenAI (Generative AI) invocations and emits telemetry data (spans and related attributes). -It supports starting, stopping, and failing LLM invocations. Classes: - TelemetryHandler: Manages GenAI invocation lifecycles and emits telemetry. @@ -28,43 +27,29 @@ Usage: handler = get_telemetry_handler() - # Create an invocation object with your request data - # The span and context_token attributes are set by the TelemetryHandler, and - # managed by the TelemetryHandler during the lifecycle of the span. - - # Use the context manager to manage the lifecycle of an LLM invocation. - with handler.llm(invocation) as invocation: - # Populate outputs and any additional attributes + # Factory method: construct and start in one call, then stop or fail. + invocation = handler.start_inference("my-provider", request_model="my-model") + invocation.input_messages = [...] + invocation.temperature = 0.7 + try: + # ... call the underlying library ... invocation.output_messages = [...] - invocation.attributes.update({"more": "attrs"}) - - # Or, if you prefer to manage the lifecycle manually - invocation = LLMInvocation( - request_model="my-model", - input_messages=[...], - provider="my-provider", - attributes={"custom": "attr"}, - ) - - # Start the invocation (opens a span) - handler.start_llm(invocation) + invocation.stop() + except Exception as exc: + invocation.fail(exc) + raise - # Populate outputs and any additional attributes, then stop (closes the span) - invocation.output_messages = [...] - invocation.attributes.update({"more": "attrs"}) - handler.stop_llm(invocation) - - # Or, in case of error - handler.fail_llm(invocation, Error(type="...", message="...")) + # Or use the context manager form — exception handling is automatic. + with handler.inference("my-provider", request_model="my-model") as invocation: + invocation.input_messages = [...] + # ... call the underlying library ... + invocation.output_messages = [...] """ from __future__ import annotations -import timeit -from contextlib import contextmanager -from typing import Iterator +from contextlib import AbstractContextManager -from opentelemetry import context as otel_context from opentelemetry._logs import ( LoggerProvider, get_logger, @@ -72,19 +57,20 @@ from opentelemetry.metrics import MeterProvider, get_meter from opentelemetry.semconv.schemas import Schemas from opentelemetry.trace import ( - Span, - SpanKind, TracerProvider, get_tracer, - set_span_in_context, ) -from opentelemetry.util.genai.metrics import InvocationMetricsRecorder -from opentelemetry.util.genai.span_utils import ( - _apply_error_attributes, - _apply_llm_finish_attributes, - _maybe_emit_llm_event, +from opentelemetry.util.genai._inference_invocation import ( + LLMInvocation, +) +from opentelemetry.util.genai._invocation import Error +from opentelemetry.util.genai.invocation import ( + EmbeddingInvocation, + InferenceInvocation, + ToolInvocation, + WorkflowInvocation, ) -from opentelemetry.util.genai.types import Error, LLMInvocation +from opentelemetry.util.genai.metrics import InvocationMetricsRecorder from opentelemetry.util.genai.version import __version__ @@ -100,95 +86,224 @@ def __init__( meter_provider: MeterProvider | None = None, logger_provider: LoggerProvider | None = None, ): + schema_url = Schemas.V1_37_0.value self._tracer = get_tracer( __name__, __version__, tracer_provider, - schema_url=Schemas.V1_37_0.value, + schema_url=schema_url, + ) + meter = get_meter( + __name__, meter_provider=meter_provider, schema_url=schema_url ) - self._metrics_recorder: InvocationMetricsRecorder | None = None - meter = get_meter(__name__, meter_provider=meter_provider) self._metrics_recorder = InvocationMetricsRecorder(meter) self._logger = get_logger( __name__, __version__, logger_provider, - schema_url=Schemas.V1_37_0.value, + schema_url=schema_url, ) - def _record_llm_metrics( + # New-style factory methods: construct + start in one call, handler stored on invocation + + def start_inference( self, - invocation: LLMInvocation, - span: Span | None = None, + provider: str, + *, + request_model: str | None = None, + server_address: str | None = None, + server_port: int | None = None, + ) -> InferenceInvocation: + """Create and start an LLM inference invocation. + + Set remaining attributes (input_messages, temperature, etc.) on the + returned invocation, then call invocation.stop() or invocation.fail(). + """ + return InferenceInvocation( + self._tracer, + self._metrics_recorder, + self._logger, + provider, + request_model=request_model, + server_address=server_address, + server_port=server_port, + ) + + def start_llm(self, invocation: LLMInvocation) -> LLMInvocation: + """Start an LLM invocation. + + .. deprecated:: + Use ``handler.start_inference()`` instead. + """ + invocation._start_with_handler( + self._tracer, self._metrics_recorder, self._logger + ) + return invocation + + def start_embedding( + self, + provider: str, *, - error_type: str | None = None, - ) -> None: - if self._metrics_recorder is None or span is None: - return - self._metrics_recorder.record( - span, - invocation, - error_type=error_type, + request_model: str | None = None, + server_address: str | None = None, + server_port: int | None = None, + ) -> EmbeddingInvocation: + """Create and start an Embedding invocation. + + Set remaining attributes (encoding_formats, etc.) on the returned + invocation, then call invocation.stop() or invocation.fail(). + """ + return EmbeddingInvocation( + self._tracer, + self._metrics_recorder, + self._logger, + provider, + request_model=request_model, + server_address=server_address, + server_port=server_port, ) - def start_llm( + def start_tool( self, - invocation: LLMInvocation, - ) -> LLMInvocation: - """Start an LLM invocation and create a pending span entry.""" - # Create a span and attach it as current; keep the token to detach later - span = self._tracer.start_span( - name=f"{invocation.operation_name} {invocation.request_model}", - kind=SpanKind.CLIENT, + name: str, + *, + arguments: object = None, + tool_call_id: str | None = None, + tool_type: str | None = None, + tool_description: str | None = None, + ) -> ToolInvocation: + """Create and start a tool invocation. + + Set tool_result on the returned invocation when done, then call + invocation.stop() or invocation.fail(). + """ + return ToolInvocation( + self._tracer, + self._metrics_recorder, + self._logger, + name, + arguments=arguments, + tool_call_id=tool_call_id, + tool_type=tool_type, + tool_description=tool_description, ) - # Record a monotonic start timestamp (seconds) for duration - # calculation using timeit.default_timer. - invocation.monotonic_start_s = timeit.default_timer() - invocation.span = span - invocation.context_token = otel_context.attach( - set_span_in_context(span) + + def start_workflow( + self, + *, + name: str | None = None, + ) -> WorkflowInvocation: + """Create and start a workflow invocation. + + Set remaining attributes on the returned invocation, then call + invocation.stop() or invocation.fail(). + """ + return WorkflowInvocation( + self._tracer, self._metrics_recorder, self._logger, name ) - return invocation def stop_llm(self, invocation: LLMInvocation) -> LLMInvocation: # pylint: disable=no-self-use - """Finalize an LLM invocation successfully and end its span.""" - if invocation.context_token is None or invocation.span is None: - # TODO: Provide feedback that this invocation was not started - return invocation - - span = invocation.span - _apply_llm_finish_attributes(span, invocation) - self._record_llm_metrics(invocation, span) - _maybe_emit_llm_event(self._logger, span, invocation) - # Detach context and end span - otel_context.detach(invocation.context_token) - span.end() + """Finalize an LLM invocation successfully and end its span. + + .. deprecated:: + Use ``handler.start_inference()`` and then ``inference.stop()`` instead. + """ + invocation._sync_to_invocation() + if invocation._inference_invocation is not None: + invocation._inference_invocation.stop() return invocation def fail_llm( # pylint: disable=no-self-use - self, invocation: LLMInvocation, error: Error + self, + invocation: LLMInvocation, + error: Error, ) -> LLMInvocation: - """Fail an LLM invocation and end its span with error status.""" - if invocation.context_token is None or invocation.span is None: - # TODO: Provide feedback that this invocation was not started - return invocation - - span = invocation.span - _apply_llm_finish_attributes(invocation.span, invocation) - _apply_error_attributes(invocation.span, error) - error_type = getattr(error.type, "__qualname__", None) - self._record_llm_metrics(invocation, span, error_type=error_type) - _maybe_emit_llm_event(self._logger, span, invocation, error) - # Detach context and end span - otel_context.detach(invocation.context_token) - span.end() + """Fail an LLM invocation and end its span with error status. + + .. deprecated:: + Use ``handler.start_inference()`` and then ``inference.fail()`` instead. + """ + invocation._sync_to_invocation() + if invocation._inference_invocation is not None: + invocation._inference_invocation.fail(error) return invocation - @contextmanager - def llm( - self, invocation: LLMInvocation | None = None - ) -> Iterator[LLMInvocation]: - """Context manager for LLM invocations. + def inference( + self, + provider: str, + *, + request_model: str | None = None, + server_address: str | None = None, + server_port: int | None = None, + ) -> AbstractContextManager[InferenceInvocation]: + """Context manager for LLM inference invocations. + + Only set data attributes on the invocation object, do not modify the span or context. + + Starts the span on entry. On normal exit, finalizes the invocation and ends the span. + If an exception occurs inside the context, marks the span as error, ends it, and + re-raises the original exception. + """ + return self.start_inference( + provider=provider, + request_model=request_model, + server_address=server_address, + server_port=server_port, + )._managed() + + def embedding( + self, + provider: str, + *, + request_model: str | None = None, + server_address: str | None = None, + server_port: int | None = None, + ) -> AbstractContextManager[EmbeddingInvocation]: + """Context manager for Embedding invocations. + + Only set data attributes on the invocation object, do not modify the span or context. + + Starts the span on entry. On normal exit, finalizes the invocation and ends the span. + If an exception occurs inside the context, marks the span as error, ends it, and + re-raises the original exception. + """ + return self.start_embedding( + provider=provider, + request_model=request_model, + server_address=server_address, + server_port=server_port, + )._managed() + + def tool( + self, + name: str, + *, + arguments: object = None, + tool_call_id: str | None = None, + tool_type: str | None = None, + tool_description: str | None = None, + ) -> AbstractContextManager[ToolInvocation]: + """Context manager for Tool invocations. + + Only set data attributes on the invocation object, do not modify the span or context. + + Starts the span on entry. On normal exit, finalizes the invocation and ends the span. + If an exception occurs inside the context, marks the span as error, ends it, and + re-raises the original exception. + """ + return self.start_tool( + name, + arguments=arguments, + tool_call_id=tool_call_id, + tool_type=tool_type, + tool_description=tool_description, + )._managed() + + def workflow( + self, + name: str | None = None, + ) -> AbstractContextManager[WorkflowInvocation]: + """Context manager for Workflow invocations. Only set data attributes on the invocation object, do not modify the span or context. @@ -196,17 +311,7 @@ def llm( If an exception occurs inside the context, marks the span as error, ends it, and re-raises the original exception. """ - if invocation is None: - invocation = LLMInvocation( - request_model="", - ) - self.start_llm(invocation) - try: - yield invocation - except Exception as exc: - self.fail_llm(invocation, Error(message=str(exc), type=type(exc))) - raise - self.stop_llm(invocation) + return self.start_workflow(name=name)._managed() def get_telemetry_handler( diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/invocation.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/invocation.py new file mode 100644 index 0000000000..4ac6426ce1 --- /dev/null +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/invocation.py @@ -0,0 +1,47 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Public re-export of all GenAI invocation types. + +Users can import everything from this single module: + + from opentelemetry.util.genai.invocation import ( + Error, + GenAIInvocation, + InferenceInvocation, + EmbeddingInvocation, + ToolInvocation, + WorkflowInvocation, + ) +""" + +from opentelemetry.util.genai._embedding_invocation import EmbeddingInvocation +from opentelemetry.util.genai._inference_invocation import InferenceInvocation +from opentelemetry.util.genai._invocation import ( + ContextToken, + Error, + GenAIInvocation, +) +from opentelemetry.util.genai._tool_invocation import ToolInvocation +from opentelemetry.util.genai._workflow_invocation import WorkflowInvocation + +__all__ = [ + "ContextToken", + "Error", + "GenAIInvocation", + "InferenceInvocation", + "EmbeddingInvocation", + "ToolInvocation", + "WorkflowInvocation", +] diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py index 075cbe60a1..144f4663b6 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py @@ -3,23 +3,18 @@ from __future__ import annotations import timeit -from typing import Dict, Optional +from typing import Optional from opentelemetry.metrics import Histogram, Meter from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAI, ) -from opentelemetry.semconv.attributes import ( - error_attributes, - server_attributes, -) -from opentelemetry.trace import Span, set_span_in_context from opentelemetry.util.genai.instruments import ( create_duration_histogram, create_token_histogram, ) -from opentelemetry.util.genai.types import LLMInvocation -from opentelemetry.util.types import AttributeValue + +from ._invocation import GenAIInvocation class InvocationMetricsRecorder: @@ -29,80 +24,30 @@ def __init__(self, meter: Meter): self._duration_histogram: Histogram = create_duration_histogram(meter) self._token_histogram: Histogram = create_token_histogram(meter) - def record( - self, - span: Optional[Span], - invocation: LLMInvocation, - *, - error_type: Optional[str] = None, - ) -> None: + def record(self, invocation: GenAIInvocation) -> None: """Record duration and token metrics for an invocation if possible.""" + attributes = invocation._get_metric_attributes() + token_counts = invocation._get_metric_token_counts() - # pylint: disable=too-many-branches - - if span is None: - return - - token_counts: list[tuple[int, str]] = [] - if invocation.input_tokens is not None: - token_counts.append( - ( - invocation.input_tokens, - GenAI.GenAiTokenTypeValues.INPUT.value, - ) - ) - if invocation.output_tokens is not None: - token_counts.append( - ( - invocation.output_tokens, - GenAI.GenAiTokenTypeValues.OUTPUT.value, - ) - ) - - attributes: Dict[str, AttributeValue] = { - GenAI.GEN_AI_OPERATION_NAME: GenAI.GenAiOperationNameValues.CHAT.value - } - if invocation.request_model: - attributes[GenAI.GEN_AI_REQUEST_MODEL] = invocation.request_model - if invocation.provider: - attributes[GenAI.GEN_AI_PROVIDER_NAME] = invocation.provider - if invocation.response_model_name: - attributes[GenAI.GEN_AI_RESPONSE_MODEL] = ( - invocation.response_model_name - ) - if invocation.server_address: - attributes[server_attributes.SERVER_ADDRESS] = ( - invocation.server_address - ) - if invocation.server_port is not None: - attributes[server_attributes.SERVER_PORT] = invocation.server_port - if invocation.metric_attributes: - attributes.update(invocation.metric_attributes) - - # Calculate duration from span timing or invocation monotonic start duration_seconds: Optional[float] = None - if invocation.monotonic_start_s is not None: + if invocation._monotonic_start_s is not None: duration_seconds = max( - timeit.default_timer() - invocation.monotonic_start_s, + timeit.default_timer() - invocation._monotonic_start_s, 0.0, ) - span_context = set_span_in_context(span) - if error_type: - attributes[error_attributes.ERROR_TYPE] = error_type - if duration_seconds is not None: self._duration_histogram.record( duration_seconds, attributes=attributes, - context=span_context, + context=invocation._span_context, ) - for token_count, token_type in token_counts: + for token_type, token_count in token_counts.items(): self._token_histogram.record( token_count, attributes=attributes | {GenAI.GEN_AI_TOKEN_TYPE: token_type}, - context=span_context, + context=invocation._span_context, ) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/span_utils.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/span_utils.py deleted file mode 100644 index 889994436f..0000000000 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/span_utils.py +++ /dev/null @@ -1,290 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import annotations - -from dataclasses import asdict -from typing import Any - -from opentelemetry._logs import Logger, LogRecord -from opentelemetry.context import get_current -from opentelemetry.semconv._incubating.attributes import ( - gen_ai_attributes as GenAI, -) -from opentelemetry.semconv.attributes import ( - error_attributes, - server_attributes, -) -from opentelemetry.trace import ( - Span, -) -from opentelemetry.trace.propagation import set_span_in_context -from opentelemetry.trace.status import Status, StatusCode -from opentelemetry.util.genai.types import ( - Error, - InputMessage, - LLMInvocation, - MessagePart, - OutputMessage, -) -from opentelemetry.util.genai.utils import ( - ContentCapturingMode, - gen_ai_json_dumps, - get_content_capturing_mode, - is_experimental_mode, - should_emit_event, -) - - -def _get_llm_common_attributes( - invocation: LLMInvocation, -) -> dict[str, Any]: - """Get common LLM attributes shared by finish() and error() paths. - - Returns a dictionary of attributes. - """ - # TODO: clean provider name to match GenAiProviderNameValues? - optional_attrs = ( - (GenAI.GEN_AI_REQUEST_MODEL, invocation.request_model), - (GenAI.GEN_AI_PROVIDER_NAME, invocation.provider), - (server_attributes.SERVER_ADDRESS, invocation.server_address), - (server_attributes.SERVER_PORT, invocation.server_port), - ) - - return { - GenAI.GEN_AI_OPERATION_NAME: invocation.operation_name, - **{key: value for key, value in optional_attrs if value is not None}, - } - - -def _get_llm_span_name(invocation: LLMInvocation) -> str: - """Get the span name for an LLM invocation.""" - return f"{invocation.operation_name} {invocation.request_model}".strip() - - -def _get_llm_messages_attributes_for_span( - input_messages: list[InputMessage], - output_messages: list[OutputMessage], - system_instruction: list[MessagePart] | None = None, -) -> dict[str, Any]: - """Get message attributes formatted for span (JSON string format). - - Returns empty dict if not in experimental mode or content capturing is disabled. - """ - if not is_experimental_mode() or get_content_capturing_mode() not in ( - ContentCapturingMode.SPAN_ONLY, - ContentCapturingMode.SPAN_AND_EVENT, - ): - return {} - - optional_attrs = ( - ( - GenAI.GEN_AI_INPUT_MESSAGES, - gen_ai_json_dumps([asdict(m) for m in input_messages]) - if input_messages - else None, - ), - ( - GenAI.GEN_AI_OUTPUT_MESSAGES, - gen_ai_json_dumps([asdict(m) for m in output_messages]) - if output_messages - else None, - ), - ( - GenAI.GEN_AI_SYSTEM_INSTRUCTIONS, - gen_ai_json_dumps([asdict(p) for p in system_instruction]) - if system_instruction - else None, - ), - ) - - return {key: value for key, value in optional_attrs if value is not None} - - -def _get_llm_messages_attributes_for_event( - input_messages: list[InputMessage], - output_messages: list[OutputMessage], - system_instruction: list[MessagePart] | None = None, -) -> dict[str, Any]: - """Get message attributes formatted for event (structured format). - - Returns empty dict if not in experimental mode or content capturing is disabled. - """ - if not is_experimental_mode() or get_content_capturing_mode() not in ( - ContentCapturingMode.EVENT_ONLY, - ContentCapturingMode.SPAN_AND_EVENT, - ): - return {} - - optional_attrs = ( - ( - GenAI.GEN_AI_INPUT_MESSAGES, - [asdict(m) for m in input_messages] if input_messages else None, - ), - ( - GenAI.GEN_AI_OUTPUT_MESSAGES, - [asdict(m) for m in output_messages] if output_messages else None, - ), - ( - GenAI.GEN_AI_SYSTEM_INSTRUCTIONS, - [asdict(p) for p in system_instruction] - if system_instruction - else None, - ), - ) - - return {key: value for key, value in optional_attrs if value is not None} - - -def _maybe_emit_llm_event( - logger: Logger | None, - span: Span, - invocation: LLMInvocation, - error: Error | None = None, -) -> None: - """Emit a gen_ai.client.inference.operation.details event to the logger. - - This function creates a LogRecord event following the semantic convention - for gen_ai.client.inference.operation.details as specified in the GenAI - event semantic conventions. - - For more details, see the semantic convention documentation: - https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-events.md#event-eventgen_aiclientinferenceoperationdetails - """ - if not is_experimental_mode() or not should_emit_event() or logger is None: - return - - # Build event attributes by reusing the attribute getter functions - attributes: dict[str, Any] = {} - attributes.update(_get_llm_common_attributes(invocation)) - attributes.update(_get_llm_request_attributes(invocation)) - attributes.update(_get_llm_response_attributes(invocation)) - attributes.update( - _get_llm_messages_attributes_for_event( - invocation.input_messages, - invocation.output_messages, - invocation.system_instruction, - ) - ) - - # Add error.type if operation ended in error - if error is not None: - attributes[error_attributes.ERROR_TYPE] = error.type.__qualname__ - - # Create and emit the event - context = set_span_in_context(span, get_current()) - event = LogRecord( - event_name="gen_ai.client.inference.operation.details", - attributes=attributes, - context=context, - ) - logger.emit(event) - - -def _apply_llm_finish_attributes( - span: Span, invocation: LLMInvocation -) -> None: - """Apply attributes/messages common to finish() paths.""" - # Update span name - span.update_name(_get_llm_span_name(invocation)) - - # Build all attributes by reusing the attribute getter functions - attributes: dict[str, Any] = {} - attributes.update(_get_llm_common_attributes(invocation)) - attributes.update(_get_llm_request_attributes(invocation)) - attributes.update(_get_llm_response_attributes(invocation)) - attributes.update( - _get_llm_messages_attributes_for_span( - invocation.input_messages, - invocation.output_messages, - invocation.system_instruction, - ) - ) - attributes.update(invocation.attributes) - - # Set all attributes on the span - if attributes: - span.set_attributes(attributes) - - -def _apply_error_attributes(span: Span, error: Error) -> None: - """Apply status and error attributes common to error() paths.""" - span.set_status(Status(StatusCode.ERROR, error.message)) - if span.is_recording(): - span.set_attribute( - error_attributes.ERROR_TYPE, error.type.__qualname__ - ) - - -def _get_llm_request_attributes( - invocation: LLMInvocation, -) -> dict[str, Any]: - """Get GenAI request semantic convention attributes.""" - optional_attrs = ( - (GenAI.GEN_AI_REQUEST_TEMPERATURE, invocation.temperature), - (GenAI.GEN_AI_REQUEST_TOP_P, invocation.top_p), - (GenAI.GEN_AI_REQUEST_FREQUENCY_PENALTY, invocation.frequency_penalty), - (GenAI.GEN_AI_REQUEST_PRESENCE_PENALTY, invocation.presence_penalty), - (GenAI.GEN_AI_REQUEST_MAX_TOKENS, invocation.max_tokens), - (GenAI.GEN_AI_REQUEST_STOP_SEQUENCES, invocation.stop_sequences), - (GenAI.GEN_AI_REQUEST_SEED, invocation.seed), - ) - - return {key: value for key, value in optional_attrs if value is not None} - - -def _get_llm_response_attributes( - invocation: LLMInvocation, -) -> dict[str, Any]: - """Get GenAI response semantic convention attributes.""" - finish_reasons: list[str] | None - if invocation.finish_reasons is not None: - finish_reasons = invocation.finish_reasons - elif invocation.output_messages: - finish_reasons = [ - message.finish_reason - for message in invocation.output_messages - if message.finish_reason - ] - else: - finish_reasons = None - - # De-duplicate finish reasons - unique_finish_reasons = ( - sorted(set(finish_reasons)) if finish_reasons else None - ) - - optional_attrs = ( - ( - GenAI.GEN_AI_RESPONSE_FINISH_REASONS, - unique_finish_reasons if unique_finish_reasons else None, - ), - (GenAI.GEN_AI_RESPONSE_MODEL, invocation.response_model_name), - (GenAI.GEN_AI_RESPONSE_ID, invocation.response_id), - (GenAI.GEN_AI_USAGE_INPUT_TOKENS, invocation.input_tokens), - (GenAI.GEN_AI_USAGE_OUTPUT_TOKENS, invocation.output_tokens), - ) - - return {key: value for key, value in optional_attrs if value is not None} - - -__all__ = [ - "_apply_llm_finish_attributes", - "_apply_error_attributes", - "_get_llm_common_attributes", - "_get_llm_request_attributes", - "_get_llm_response_attributes", - "_get_llm_span_name", - "_maybe_emit_llm_event", -] diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py index 045a65b372..103c168023 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py @@ -14,20 +14,17 @@ from __future__ import annotations -from contextvars import Token -from dataclasses import dataclass, field +from dataclasses import dataclass from enum import Enum -from typing import Any, Literal, Type, Union +from typing import TYPE_CHECKING, Any, Literal, Type, Union -from typing_extensions import TypeAlias - -from opentelemetry.context import Context -from opentelemetry.semconv._incubating.attributes import ( - gen_ai_attributes as GenAI, -) -from opentelemetry.trace import Span - -ContextToken: TypeAlias = Token[Context] +if TYPE_CHECKING: + from opentelemetry.util.genai._inference_invocation import ( # pylint: disable=useless-import-alias + LLMInvocation as LLMInvocation, # noqa: PLC0414 + ) + from opentelemetry.util.genai._invocation import ( # pylint: disable=useless-import-alias + GenAIInvocation as GenAIInvocation, # noqa: PLC0414 + ) class ContentCapturingMode(Enum): @@ -42,8 +39,22 @@ class ContentCapturingMode(Enum): @dataclass() -class ToolCall: - """Represents a tool call requested by the model +class GenericPart: + """Used for provider-specific message part types that don't match + the standard MessagePart types defined in semantic conventions. Wrap custom + types with GenericPart(value=...) to explicitly opt-in to non-standard types. + This will be removed in a future version when all instrumentations use core types.""" + + value: Any + type: Literal["generic"] = "generic" + + +@dataclass() +class ToolCallRequest: + """Represents a tool call requested by the model (message part only). + + Use this for tool calls in message history. For execution tracking with spans + and metrics, use ToolInvocation instead. This model is specified as part of semconv in `GenAI messages Python models - ToolCallRequestPart `__. @@ -68,6 +79,41 @@ class ToolCallResponse: type: Literal["tool_call_response"] = "tool_call_response" +@dataclass() +class ServerToolCall: + """Represents a server-side tool call. + + Server tool calls are executed by the model provider on the server side rather + than by the client application. Provider-specific tools (e.g., code_interpreter, + web_search) can have well-defined schemas defined by the respective providers. + + This model is specified as part of semconv in `GenAI messages Python models - ServerToolCallPart + `__. + """ + + name: str + server_tool_call: Any + id: str | None = None + type: Literal["server_tool_call"] = "server_tool_call" + + +@dataclass() +class ServerToolCallResponse: + """Represents a server-side tool call response. + + Contains the outcome and details of a server tool execution. Provider-specific + tools (e.g., code_interpreter, web_search) can have well-defined response schemas + defined by the respective providers. + + This model is specified as part of semconv in `GenAI messages Python models - ServerToolCallResponsePart + `__. + """ + + server_tool_call_response: Any + id: str | None = None + type: Literal["server_tool_call_response"] = "server_tool_call_response" + + @dataclass() class Text: """Represents text content sent to or received from the model @@ -158,7 +204,16 @@ class GenericToolDefinition: ToolDefinition = Union[FunctionToolDefinition, GenericToolDefinition] MessagePart = Union[ - Text, ToolCall, ToolCallResponse, Blob, File, Uri, Reasoning, Any + Text, + ToolCallRequest, + ToolCallResponse, + ServerToolCall, + ServerToolCallResponse, + Blob, + File, + Uri, + Reasoning, + GenericPart, # For provider-specific types; prefer standard types above ] @@ -180,83 +235,21 @@ class OutputMessage: finish_reason: str | FinishReason -def _new_input_messages() -> list[InputMessage]: - return [] - - -def _new_output_messages() -> list[OutputMessage]: - return [] - - -def _new_system_instruction() -> list[MessagePart]: - return [] - - -def _new_str_any_dict() -> dict[str, Any]: - return {} - - @dataclass -class GenAIInvocation: - context_token: ContextToken | None = None - span: Span | None = None - attributes: dict[str, Any] = field(default_factory=_new_str_any_dict) - +class Error: + message: str + type: Type[BaseException] -@dataclass -class LLMInvocation(GenAIInvocation): - """ - Represents a single LLM call invocation. When creating an LLMInvocation object, - only update the data attributes. The span and context_token attributes are - set by the TelemetryHandler. - """ - request_model: str | None = None - # Chat by default - operation_name: str = GenAI.GenAiOperationNameValues.CHAT.value - input_messages: list[InputMessage] = field( - default_factory=_new_input_messages - ) - output_messages: list[OutputMessage] = field( - default_factory=_new_output_messages - ) - system_instruction: list[MessagePart] = field( - default_factory=_new_system_instruction - ) - provider: str | None = None - response_model_name: str | None = None - response_id: str | None = None - finish_reasons: list[str] | None = None - input_tokens: int | None = None - output_tokens: int | None = None - attributes: dict[str, Any] = field(default_factory=_new_str_any_dict) - """ - Additional attributes to set on spans and/or events. These attributes - will not be set on metrics. - """ - metric_attributes: dict[str, Any] = field( - default_factory=_new_str_any_dict - ) - """ - Additional attributes to set on metrics. Must be of a low cardinality. - These attributes will not be set on spans or events. - """ - temperature: float | None = None - top_p: float | None = None - frequency_penalty: float | None = None - presence_penalty: float | None = None - max_tokens: int | None = None - stop_sequences: list[str] | None = None - seed: int | None = None - server_address: str | None = None - server_port: int | None = None - # Monotonic start time in seconds (from timeit.default_timer) used - # for duration calculations to avoid mixing clock sources. This is - # populated by the TelemetryHandler when starting an invocation. - monotonic_start_s: float | None = None +def __getattr__(name: str) -> object: + if name == "GenAIInvocation": + import opentelemetry.util.genai.invocation as _inv # noqa: PLC0415 # pylint: disable=import-outside-toplevel + return _inv.GenAIInvocation + if name == "LLMInvocation": + from opentelemetry.util.genai._inference_invocation import ( # noqa: PLC0415 # pylint: disable=import-outside-toplevel + LLMInvocation, + ) -@dataclass -class Error: - message: str - type: Type[BaseException] + return LLMInvocation + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/utils.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/utils.py index 616d9ab471..8b6c5e8a2a 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/utils.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/utils.py @@ -111,6 +111,18 @@ def should_emit_event() -> bool: return False +def should_capture_content_on_spans_in_experimental_mode() -> bool: + """Return True when content conversion should be performed.""" + if not is_experimental_mode(): + return False + mode = get_content_capturing_mode() + if mode == ContentCapturingMode.NO_CONTENT: + return False + if mode == ContentCapturingMode.EVENT_ONLY and not should_emit_event(): + return False + return True + + class _GenAiJsonEncoder(json.JSONEncoder): def default(self, o: Any) -> Any: if isinstance(o, bytes): diff --git a/util/opentelemetry-util-genai/tests/test_events_options.py b/util/opentelemetry-util-genai/tests/test_events_options.py index 78476de880..1095d39d24 100644 --- a/util/opentelemetry-util-genai/tests/test_events_options.py +++ b/util/opentelemetry-util-genai/tests/test_events_options.py @@ -25,6 +25,7 @@ OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT, ) from opentelemetry.util.genai.utils import ( + should_capture_content_on_spans_in_experimental_mode, should_emit_event, ) @@ -206,3 +207,45 @@ def test_should_emit_event_user_setting_overrides_default_for_no_content( ): # pylint: disable=no-self-use # User explicitly setting emit_event="true" should override the default (False for NO_CONTENT) assert should_emit_event() is True + + +class TestShouldCaptureContent(unittest.TestCase): + @patch_env_vars( + stability_mode="default", + content_capturing="SPAN_AND_EVENT", + emit_event="true", + ) + def test_should_capture_content_on_spans_in_experimental_mode_false_when_not_experimental( + self, + ): # pylint: disable=no-self-use + assert should_capture_content_on_spans_in_experimental_mode() is False + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="NO_CONTENT", + emit_event="true", + ) + def test_should_capture_content_on_spans_in_experimental_mode_false_when_no_content( + self, + ): # pylint: disable=no-self-use + assert should_capture_content_on_spans_in_experimental_mode() is False + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="EVENT_ONLY", + emit_event="false", + ) + def test_should_capture_content_on_spans_in_experimental_mode_false_when_event_only_but_event_disabled( + self, + ): # pylint: disable=no-self-use + assert should_capture_content_on_spans_in_experimental_mode() is False + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="SPAN_ONLY", + emit_event="", + ) + def test_should_capture_content_on_spans_in_experimental_mode_true_for_span_only( + self, + ): # pylint: disable=no-self-use + assert should_capture_content_on_spans_in_experimental_mode() is True diff --git a/util/opentelemetry-util-genai/tests/test_handler_metrics.py b/util/opentelemetry-util-genai/tests/test_handler_metrics.py index abfba3c7e6..233edb9050 100644 --- a/util/opentelemetry-util-genai/tests/test_handler_metrics.py +++ b/util/opentelemetry-util-genai/tests/test_handler_metrics.py @@ -1,54 +1,41 @@ from __future__ import annotations from typing import Any, Dict, List -from unittest import TestCase from unittest.mock import patch -from opentelemetry.sdk.metrics import MeterProvider -from opentelemetry.sdk.metrics.export import InMemoryMetricReader -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import SimpleSpanProcessor -from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( - InMemorySpanExporter, -) from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAI, ) +from opentelemetry.semconv.schemas import Schemas +from opentelemetry.test.test_base import TestBase from opentelemetry.util.genai.handler import TelemetryHandler -from opentelemetry.util.genai.types import Error, LLMInvocation +from opentelemetry.util.genai.types import Error +_DEFAULT_SCHEMA_URL = Schemas.V1_37_0.value + +SCOPE = "opentelemetry.util.genai.handler" -class TelemetryHandlerMetricsTest(TestCase): - def setUp(self) -> None: - self.metric_reader = InMemoryMetricReader() - self.meter_provider = MeterProvider( - metric_readers=[self.metric_reader] - ) - self.span_exporter = InMemorySpanExporter() - self.tracer_provider = TracerProvider() - self.tracer_provider.add_span_processor( - SimpleSpanProcessor(self.span_exporter) - ) +class TelemetryHandlerMetricsTest(TestBase): def test_stop_llm_records_duration_and_tokens(self) -> None: handler = TelemetryHandler( tracer_provider=self.tracer_provider, meter_provider=self.meter_provider, ) - invocation = LLMInvocation(request_model="model", provider="prov") - invocation.input_tokens = 5 - invocation.output_tokens = 7 # Patch default_timer during start to ensure monotonic_start_s with patch("timeit.default_timer", return_value=1000.0): - handler.start_llm(invocation) + invocation = handler.start_inference("prov", request_model="model") + invocation.input_tokens = 5 + invocation.output_tokens = 7 # Simulate 2 seconds of elapsed monotonic time (seconds) with patch( "timeit.default_timer", return_value=1002.0, ): - handler.stop_llm(invocation) + invocation.stop() + self._assert_metric_scope_schema_urls(_DEFAULT_SCHEMA_URL) metrics = self._harvest_metrics() self.assertIn("gen_ai.client.operation.duration", metrics) duration_points = metrics["gen_ai.client.operation.duration"] @@ -92,18 +79,21 @@ def test_stop_llm_records_duration_and_tokens_with_additional_attributes( meter_provider=self.meter_provider, ) - invocation = LLMInvocation(request_model="model", provider="prov") + invocation = handler.start_inference( + "prov", + request_model="model", + server_address="custom.server.com", + server_port=42, + ) invocation.input_tokens = 5 invocation.output_tokens = 7 - invocation.server_address = "custom.server.com" - invocation.server_port = 42 - handler.start_llm(invocation) invocation.metric_attributes = { "custom.attribute": "custom_value", } invocation.attributes = {"should not be on metrics": "value"} - handler.stop_llm(invocation) + invocation.stop() + self._assert_metric_scope_schema_urls(_DEFAULT_SCHEMA_URL) metrics = self._harvest_metrics() self.assertIn("gen_ai.client.operation.duration", metrics) duration_points = metrics["gen_ai.client.operation.duration"] @@ -126,19 +116,19 @@ def test_fail_llm_records_error_and_available_tokens(self) -> None: tracer_provider=self.tracer_provider, meter_provider=self.meter_provider, ) - invocation = LLMInvocation(request_model="err-model", provider=None) - invocation.input_tokens = 11 # Patch default_timer during start to ensure monotonic_start_s with patch("timeit.default_timer", return_value=2000.0): - handler.start_llm(invocation) + invocation = handler.start_inference("", request_model="err-model") + invocation.input_tokens = 11 error = Error(message="boom", type=ValueError) with patch( "timeit.default_timer", return_value=2001.0, ): - handler.fail_llm(invocation, error) + invocation.fail(error) + self._assert_metric_scope_schema_urls(_DEFAULT_SCHEMA_URL) metrics = self._harvest_metrics() self.assertIn("gen_ai.client.operation.duration", metrics) duration_points = metrics["gen_ai.client.operation.duration"] @@ -163,17 +153,173 @@ def test_fail_llm_records_error_and_available_tokens(self) -> None: ) self.assertAlmostEqual(token_point.sum, 11.0, places=3) - def _harvest_metrics(self) -> Dict[str, List[Any]]: - try: - self.meter_provider.force_flush() - except Exception: # pylint: disable=broad-except - pass - self.metric_reader.collect() + def _harvest_metrics( + self, + ) -> Dict[str, List[Any]]: + """Returns (metrics_by_name, resource_metrics). + + metrics_by_name maps metric name to list of data points. + resource_metrics is the raw ResourceMetrics list for scope-level + assertions (e.g. schema_url). + """ + metrics = self.get_sorted_metrics() metrics_by_name: Dict[str, List[Any]] = {} - data = self.metric_reader.get_metrics_data() - for resource_metric in (data and data.resource_metrics) or []: - for scope_metric in resource_metric.scope_metrics or []: - for metric in scope_metric.metrics or []: - points = metric.data.data_points or [] - metrics_by_name.setdefault(metric.name, []).extend(points) + for metric in metrics or []: + points = metric.data.data_points or [] + metrics_by_name.setdefault(metric.name, []).extend(points) return metrics_by_name + + def _assert_metric_scope_schema_urls( + self, expected_schema_url: str + ) -> None: + for ( + resource_metric + ) in self.memory_metrics_reader.get_metrics_data().resource_metrics: + for scope_metric in resource_metric.scope_metrics: + if scope_metric.scope.name != SCOPE: + continue + self.assertEqual( + scope_metric.scope.schema_url, expected_schema_url + ) + + def test_stop_embedding_records_duration_and_tokens(self) -> None: + """Verify embedding invocations record duration and input token metrics.""" + handler = TelemetryHandler( + tracer_provider=self.tracer_provider, + meter_provider=self.meter_provider, + ) + # Patch default_timer during start to ensure monotonic_start_s + with patch("timeit.default_timer", return_value=1000.0): + invocation = handler.start_embedding( + "embed-prov", request_model="embed-model" + ) + invocation.input_tokens = 100 + + # Simulate 1.5 seconds of elapsed monotonic time + with patch("timeit.default_timer", return_value=1001.5): + invocation.stop() + + self._assert_metric_scope_schema_urls(_DEFAULT_SCHEMA_URL) + metrics = self._harvest_metrics() + + # Duration should be recorded + self.assertIn("gen_ai.client.operation.duration", metrics) + duration_points = metrics["gen_ai.client.operation.duration"] + self.assertEqual(len(duration_points), 1) + duration_point = duration_points[0] + self.assertEqual( + duration_point.attributes[GenAI.GEN_AI_OPERATION_NAME], + GenAI.GenAiOperationNameValues.EMBEDDINGS.value, + ) + self.assertEqual( + duration_point.attributes[GenAI.GEN_AI_REQUEST_MODEL], + "embed-model", + ) + self.assertEqual( + duration_point.attributes[GenAI.GEN_AI_PROVIDER_NAME], "embed-prov" + ) + self.assertAlmostEqual(duration_point.sum, 1.5, places=3) + + # Token metrics should be recorded for embedding (input only) + self.assertIn("gen_ai.client.token.usage", metrics) + token_points = metrics["gen_ai.client.token.usage"] + self.assertEqual(len(token_points), 1) # Only input tokens + token_point = token_points[0] + self.assertEqual( + token_point.attributes[GenAI.GEN_AI_TOKEN_TYPE], + GenAI.GenAiTokenTypeValues.INPUT.value, + ) + self.assertAlmostEqual(token_point.sum, 100.0, places=3) + + def test_stop_embedding_records_duration_with_additional_attributes( + self, + ) -> None: + """Verify embedding metrics include server and custom attributes.""" + handler = TelemetryHandler( + tracer_provider=self.tracer_provider, + meter_provider=self.meter_provider, + ) + invocation = handler.start_embedding( + "embed-prov", + request_model="embed-model", + server_address="embed.server.com", + server_port=8080, + ) + invocation.metric_attributes = {"custom.embed.attr": "embed_value"} + invocation.response_model_name = "embed-response-model" + invocation.stop() + + self._assert_metric_scope_schema_urls(_DEFAULT_SCHEMA_URL) + metrics = self._harvest_metrics() + + self.assertIn("gen_ai.client.operation.duration", metrics) + duration_points = metrics["gen_ai.client.operation.duration"] + self.assertEqual(len(duration_points), 1) + duration_point = duration_points[0] + + self.assertEqual( + duration_point.attributes["server.address"], "embed.server.com" + ) + self.assertEqual(duration_point.attributes["server.port"], 8080) + self.assertEqual( + duration_point.attributes["custom.embed.attr"], "embed_value" + ) + self.assertEqual( + duration_point.attributes[GenAI.GEN_AI_RESPONSE_MODEL], + "embed-response-model", + ) + + def test_fail_embedding_records_error_and_duration(self) -> None: + """Verify embedding failure records error type and duration.""" + handler = TelemetryHandler( + tracer_provider=self.tracer_provider, + meter_provider=self.meter_provider, + ) + with patch("timeit.default_timer", return_value=3000.0): + invocation = handler.start_embedding( + "embed-prov", request_model="embed-err-model" + ) + + error = Error(message="embedding failed", type=RuntimeError) + with patch("timeit.default_timer", return_value=3002.5): + invocation.fail(error) + + self._assert_metric_scope_schema_urls(_DEFAULT_SCHEMA_URL) + metrics = self._harvest_metrics() + + self.assertIn("gen_ai.client.operation.duration", metrics) + duration_points = metrics["gen_ai.client.operation.duration"] + self.assertEqual(len(duration_points), 1) + duration_point = duration_points[0] + + self.assertEqual( + duration_point.attributes.get("error.type"), "RuntimeError" + ) + self.assertEqual( + duration_point.attributes.get(GenAI.GEN_AI_REQUEST_MODEL), + "embed-err-model", + ) + self.assertAlmostEqual(duration_point.sum, 2.5, places=3) + + # Token metrics should NOT be recorded when input_tokens is not set + self.assertNotIn("gen_ai.client.token.usage", metrics) + + def test_stop_embedding_without_tokens(self) -> None: + """Verify embedding without input_tokens does not record token metrics.""" + handler = TelemetryHandler( + tracer_provider=self.tracer_provider, + meter_provider=self.meter_provider, + ) + invocation = handler.start_embedding( + "embed-prov", request_model="embed-model" + ) + # input_tokens is not set + invocation.stop() + + metrics = self._harvest_metrics() + + # Duration should be recorded + self.assertIn("gen_ai.client.operation.duration", metrics) + + # Token metrics should NOT be recorded when input_tokens is not set + self.assertNotIn("gen_ai.client.token.usage", metrics) diff --git a/util/opentelemetry-util-genai/tests/test_handler_workflow.py b/util/opentelemetry-util-genai/tests/test_handler_workflow.py new file mode 100644 index 0000000000..3bf689a14b --- /dev/null +++ b/util/opentelemetry-util-genai/tests/test_handler_workflow.py @@ -0,0 +1,227 @@ +from __future__ import annotations + +from unittest import TestCase +from unittest.mock import patch + +import pytest + +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, +) +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAI, +) +from opentelemetry.trace import INVALID_SPAN, SpanKind +from opentelemetry.trace.status import StatusCode +from opentelemetry.util.genai.handler import TelemetryHandler +from opentelemetry.util.genai.invocation import WorkflowInvocation +from opentelemetry.util.genai.types import ( + Error, + InputMessage, + OutputMessage, + Text, +) + + +class _WorkflowTestBase(TestCase): + """Shared setUp for workflow handler tests.""" + + def setUp(self) -> None: + self.span_exporter = InMemorySpanExporter() + self.tracer_provider = TracerProvider() + self.tracer_provider.add_span_processor( + SimpleSpanProcessor(self.span_exporter) + ) + self.handler = TelemetryHandler( + tracer_provider=self.tracer_provider, + ) + + def _get_finished_spans(self): + return self.span_exporter.get_finished_spans() + + +class TelemetryHandlerWorkflowTest(_WorkflowTestBase): + # ------------------------------------------------------------------ + # start_workflow + # ------------------------------------------------------------------ + + def test_start_workflow_creates_span(self) -> None: + invocation = self.handler.start_workflow(name="my_workflow") + self.assertIsNot(invocation.span, INVALID_SPAN) + invocation.stop() + + def test_start_workflow_span_name(self) -> None: + invocation = self.handler.start_workflow(name="my_pipeline") + invocation.stop() + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].name, "invoke_workflow my_pipeline") + + def test_start_workflow_span_name_without_name(self) -> None: + invocation = self.handler.start_workflow(name=None) + invocation.stop() + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].name, "invoke_workflow") + + def test_start_workflow_span_kind_is_internal(self) -> None: + invocation = self.handler.start_workflow(name="wf") + invocation.stop() + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].kind, SpanKind.INTERNAL) + + def test_start_workflow_records_monotonic_start(self) -> None: + with patch("timeit.default_timer", return_value=500.0): + invocation = self.handler.start_workflow(name="wf") + self.assertEqual(invocation._monotonic_start_s, 500.0) + invocation.stop() + + # ------------------------------------------------------------------ + # stop_workflow + # ------------------------------------------------------------------ + + def test_stop_workflow_ends_span(self) -> None: + invocation = self.handler.start_workflow(name="wf") + invocation.stop() + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + + def test_stop_workflow_sets_operation_name_attribute(self) -> None: + invocation = self.handler.start_workflow(name="wf") + invocation.stop() + + spans = self._get_finished_spans() + self.assertEqual( + spans[0].attributes[GenAI.GEN_AI_OPERATION_NAME], + "invoke_workflow", + ) + + def test_stop_workflow_sets_custom_attributes(self) -> None: + invocation = self.handler.start_workflow(name="wf") + invocation.attributes["custom.key"] = "custom_value" + invocation.stop() + + spans = self._get_finished_spans() + self.assertEqual(spans[0].attributes["custom.key"], "custom_value") + + def test_stop_workflow_returns_invocation(self) -> None: + invocation = self.handler.start_workflow(name="wf") + invocation.stop() + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + + # ------------------------------------------------------------------ + # fail_workflow + # ------------------------------------------------------------------ + + def test_fail_workflow_sets_error_status(self) -> None: + invocation = self.handler.start_workflow(name="wf") + error = Error(message="something broke", type=RuntimeError) + invocation.fail(error) + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].status.status_code, StatusCode.ERROR) + self.assertEqual(spans[0].status.description, "something broke") + + def test_fail_workflow_sets_error_type_attribute(self) -> None: + invocation = self.handler.start_workflow(name="wf") + error = Error(message="bad", type=ValueError) + invocation.fail(error) + + spans = self._get_finished_spans() + self.assertEqual(spans[0].attributes["error.type"], "ValueError") + + def test_fail_workflow_sets_operation_name_attribute(self) -> None: + invocation = self.handler.start_workflow(name="wf") + error = Error(message="fail", type=TypeError) + invocation.fail(error) + + spans = self._get_finished_spans() + self.assertEqual( + spans[0].attributes[GenAI.GEN_AI_OPERATION_NAME], + "invoke_workflow", + ) + + def test_fail_workflow_ends_span(self) -> None: + invocation = self.handler.start_workflow(name="wf") + invocation.fail(Error(message="err", type=RuntimeError)) + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].status.status_code, StatusCode.ERROR) + + +class TelemetryHandlerWorkflowContextManagerTest(_WorkflowTestBase): + # ------------------------------------------------------------------ + # workflow context manager + # ------------------------------------------------------------------ + + def test_workflow_context_manager_creates_and_ends_span(self) -> None: + with self.handler.workflow(name="ctx_wf") as inv: + self.assertIsNot(inv.span, INVALID_SPAN) + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].name, "invoke_workflow ctx_wf") + + def test_workflow_context_manager_default_invocation(self) -> None: + with self.handler.workflow() as inv: + self.assertIsInstance(inv, WorkflowInvocation) + self.assertIsNone(inv.name) + self.assertEqual(inv._operation_name, "invoke_workflow") + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + + def test_workflow_context_manager_sets_attributes_on_span(self) -> None: + with self.handler.workflow("wf") as inv: + inv.attributes["my.attr"] = "hello" + + spans = self._get_finished_spans() + self.assertEqual(spans[0].attributes["my.attr"], "hello") + + def test_workflow_context_manager_reraises_exception(self) -> None: + with pytest.raises(ValueError, match="test error"): + with self.handler.workflow("wf"): + raise ValueError("test error") + + def test_workflow_context_manager_marks_error_on_exception(self) -> None: + with pytest.raises(ValueError): + with self.handler.workflow("wf"): + raise ValueError("boom") + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].status.status_code, StatusCode.ERROR) + self.assertEqual(spans[0].status.description, "boom") + self.assertEqual(spans[0].attributes["error.type"], "ValueError") + + def test_workflow_context_manager_success_has_unset_status(self) -> None: + with self.handler.workflow("wf"): + pass + + spans = self._get_finished_spans() + self.assertEqual(spans[0].status.status_code, StatusCode.UNSET) + + def test_workflow_context_manager_with_messages(self) -> None: + inp = InputMessage(role="user", parts=[Text(content="hello")]) + out = OutputMessage( + role="assistant", parts=[Text(content="hi")], finish_reason="stop" + ) + with self.handler.workflow("msg_wf") as inv: + inv.input_messages = [inp] + inv.output_messages = [out] + + spans = self._get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual( + spans[0].attributes[GenAI.GEN_AI_OPERATION_NAME], + "invoke_workflow", + ) diff --git a/util/opentelemetry-util-genai/tests/test_toolcall.py b/util/opentelemetry-util-genai/tests/test_toolcall.py new file mode 100644 index 0000000000..abf7f637ae --- /dev/null +++ b/util/opentelemetry-util-genai/tests/test_toolcall.py @@ -0,0 +1,138 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for ToolCallRequest and ToolInvocation inheritance structure""" + +import pytest + +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.util.genai.handler import TelemetryHandler +from opentelemetry.util.genai.invocation import GenAIInvocation +from opentelemetry.util.genai.types import ( + InputMessage, + ServerToolCall, + ServerToolCallResponse, + ToolCallRequest, +) + + +def _make_handler() -> TelemetryHandler: + return TelemetryHandler(tracer_provider=TracerProvider()) + + +def test_toolcallrequest_is_message_part(): + """ToolCallRequest is for message parts only""" + tcr = ToolCallRequest( + arguments={"location": "Paris"}, name="get_weather", id="call_123" + ) + msg = InputMessage(role="user", parts=[tcr]) + assert len(msg.parts) == 1 + + +def test_toolcall_inherits_from_genaiinvocation(): + """ToolInvocation inherits from GenAIInvocation for lifecycle management""" + handler = _make_handler() + tc = handler.start_tool("get_weather", arguments={"city": "Paris"}) + assert isinstance(tc, GenAIInvocation) + assert not isinstance(tc, ToolCallRequest) + tc.stop() + + +def test_toolcall_has_attributes_dict(): + """ToolInvocation inherits attributes dict from GenAIInvocation""" + handler = _make_handler() + tc = handler.start_tool("test") + tc.attributes["custom.key"] = "value" + assert tc.attributes["custom.key"] == "value" + tc.stop() + + +def test_toolcallrequest_in_message_part_union(): + """ToolCallRequest (not ToolInvocation) is the correct type for message parts""" + tc = ToolCallRequest( + name="get_weather", arguments={"city": "Paris"}, id="call_123" + ) + msg = InputMessage(role="assistant", parts=[tc]) + assert len(msg.parts) == 1 + assert isinstance(msg.parts[0], ToolCallRequest) + assert not isinstance(msg.parts[0], GenAIInvocation) + + +def test_toolcall_operation_name(): + """ToolInvocation operation_name is fixed to execute_tool""" + handler = _make_handler() + tc = handler.start_tool("my_tool") + assert tc._operation_name == "execute_tool" + tc.stop() + + +def test_server_tool_call_basic(): + """ServerToolCall can be created with required fields""" + stc = ServerToolCall( + name="code_interpreter", + server_tool_call={"type": "code_interpreter", "code": "print(1)"}, + ) + assert stc.name == "code_interpreter" + assert stc.server_tool_call == { + "type": "code_interpreter", + "code": "print(1)", + } + assert stc.id is None + assert stc.type == "server_tool_call" + + +def test_server_tool_call_with_id(): + """ServerToolCall can have an optional id""" + stc = ServerToolCall( + name="web_search", + server_tool_call={"type": "web_search", "query": "weather"}, + id="stc_001", + ) + assert stc.id == "stc_001" + + +def test_server_tool_call_response_basic(): + """ServerToolCallResponse can be created with required fields""" + stcr = ServerToolCallResponse( + server_tool_call_response={ + "type": "code_interpreter", + "output": "1\n", + }, + ) + assert stcr.server_tool_call_response == { + "type": "code_interpreter", + "output": "1\n", + } + assert stcr.id is None + assert stcr.type == "server_tool_call_response" + + +def test_server_tool_call_in_message(): + """ServerToolCall and ServerToolCallResponse work as MessageParts""" + stc = ServerToolCall( + name="code_interpreter", + server_tool_call={"type": "code_interpreter", "code": "x = 1"}, + ) + stcr = ServerToolCallResponse( + server_tool_call_response={"type": "code_interpreter", "output": ""}, + id="stc_001", + ) + msg = InputMessage(role="assistant", parts=[stc, stcr]) + assert len(msg.parts) == 2 + assert isinstance(msg.parts[0], ServerToolCall) + assert isinstance(msg.parts[1], ServerToolCallResponse) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/util/opentelemetry-util-genai/tests/test_upload.py b/util/opentelemetry-util-genai/tests/test_upload.py index dd87b971e0..c9611abf19 100644 --- a/util/opentelemetry-util-genai/tests/test_upload.py +++ b/util/opentelemetry-util-genai/tests/test_upload.py @@ -44,7 +44,7 @@ types.InputMessage( role="assistant", parts=[ - types.ToolCall( + types.ToolCallRequest( id="get_capital_0", name="get_capital", arguments={"city": "Paris"}, @@ -102,6 +102,8 @@ def setUp(self): self.hook = UploadCompletionHook( base_path=BASE_PATH, max_queue_size=MAXSIZE, lru_cache_max_size=5 ) + # 1 upload is done when creating the UploadHook to ensure upload works. Reset mock. + self.mock_fs.reset_mock() def tearDown(self) -> None: self.hook.shutdown() @@ -145,6 +147,18 @@ def test_upload_then_shutdown(self): "should have uploaded 4 files", ) + def test_failed_upload_causes_initializer_to_throw(self): + self.mock_fs.open.side_effect = ValueError("Failed for some reason!") + with self.assertRaisesRegex( + ValueError, + "Failed to write file to the following path, upload is not working:", + ): + UploadCompletionHook( + base_path=BASE_PATH, + max_queue_size=MAXSIZE, + lru_cache_max_size=5, + ) + def test_lru_cache_works(self): record = LogRecord() self.hook.on_completion( diff --git a/util/opentelemetry-util-genai/tests/test_utils.py b/util/opentelemetry-util-genai/tests/test_utils.py index 851670026d..31629c5b0c 100644 --- a/util/opentelemetry-util-genai/tests/test_utils.py +++ b/util/opentelemetry-util-genai/tests/test_utils.py @@ -25,7 +25,7 @@ ) from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk._logs.export import ( - InMemoryLogRecordExporter, + InMemoryLogExporter, SimpleLogRecordProcessor, ) from opentelemetry.sdk.trace import ReadableSpan, TracerProvider @@ -49,9 +49,7 @@ from opentelemetry.util.genai.handler import get_telemetry_handler from opentelemetry.util.genai.types import ( ContentCapturingMode, - Error, InputMessage, - LLMInvocation, MessagePart, OutputMessage, Text, @@ -222,7 +220,7 @@ def setUp(self): tracer_provider.add_span_processor( SimpleSpanProcessor(self.span_exporter) ) - self.log_exporter = InMemoryLogRecordExporter() + self.log_exporter = InMemoryLogExporter() logger_provider = LoggerProvider() logger_provider.add_log_record_processor( SimpleLogRecordProcessor(self.log_exporter) @@ -248,25 +246,23 @@ def test_llm_start_and_stop_creates_span(self): # pylint: disable=no-self-use chat_generation = _create_output_message("hello back") system_instruction = _create_system_instruction() - with self.telemetry_handler.llm() as invocation: - for attr, value in { - "request_model": "test-model", - "input_messages": [message], - "system_instruction": system_instruction, - "provider": "test-provider", - "attributes": {"custom_attr": "value"}, - "temperature": 0.5, - "top_p": 0.9, - "stop_sequences": ["stop"], - "finish_reasons": ["stop"], - "response_model_name": "test-response-model", - "response_id": "response-id", - "input_tokens": 321, - "output_tokens": 654, - "server_address": "custom.server.com", - "server_port": 42, - }.items(): - setattr(invocation, attr, value) + with self.telemetry_handler.inference( + "test-provider", + request_model="test-model", + server_address="custom.server.com", + server_port=42, + ) as invocation: + invocation.input_messages = [message] + invocation.system_instruction = system_instruction + invocation.attributes = {"custom_attr": "value"} + invocation.temperature = 0.5 + invocation.top_p = 0.9 + invocation.stop_sequences = ["stop"] + invocation.finish_reasons = ["stop"] + invocation.response_model_name = "test-response-model" + invocation.response_id = "response-id" + invocation.input_tokens = 321 + invocation.output_tokens = 654 assert invocation.span is not None invocation.output_messages = [chat_generation] invocation.attributes.update({"extra": "info"}) @@ -331,18 +327,15 @@ def test_llm_manual_start_and_stop_creates_span(self): message = _create_input_message("hi") chat_generation = _create_output_message("ok") - invocation = LLMInvocation( - request_model="manual-model", - input_messages=[message], - provider="test-provider", - attributes={"manual": True}, + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="manual-model" ) - - self.telemetry_handler.start_llm(invocation) + invocation.input_messages = [message] + invocation.attributes["manual"] = True assert invocation.span is not None invocation.output_messages = [chat_generation] invocation.attributes.update({"extra_manual": "yes"}) - self.telemetry_handler.stop_llm(invocation) + invocation.stop() span = _get_single_span(self.span_exporter) assert span.name == "chat manual-model" @@ -365,19 +358,16 @@ def test_llm_manual_start_and_stop_creates_span(self): ) def test_llm_span_finish_reasons_without_output_messages(self): - invocation = LLMInvocation( - request_model="model-without-output", - provider="test-provider", - finish_reasons=["length"], - response_model_name="alt-model", - response_id="resp-001", - input_tokens=12, - output_tokens=34, - ) - - self.telemetry_handler.start_llm(invocation) + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="model-without-output" + ) + invocation.finish_reasons = ["length"] + invocation.response_model_name = "alt-model" + invocation.response_id = "resp-001" + invocation.input_tokens = 12 + invocation.output_tokens = 34 assert invocation.span is not None - self.telemetry_handler.stop_llm(invocation) + invocation.stop() span = _get_single_span(self.span_exporter) _assert_span_time_order(span) @@ -396,55 +386,46 @@ def test_llm_span_finish_reasons_without_output_messages(self): }, ) - def test_llm_span_finish_reasons_deduplicated_from_invocation(self): - invocation = LLMInvocation( - request_model="model-dedup", - provider="test-provider", - finish_reasons=["stop", "length", "stop"], + def test_llm_span_finish_reasons_from_invocation(self): + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="model-reasons" ) - - self.telemetry_handler.start_llm(invocation) + invocation.finish_reasons = ["stop", "length", "stop"] assert invocation.span is not None - self.telemetry_handler.stop_llm(invocation) + invocation.stop() span = _get_single_span(self.span_exporter) attrs = _get_span_attributes(span) self.assertEqual( attrs[GenAI.GEN_AI_RESPONSE_FINISH_REASONS], - ("length", "stop"), + ("stop", "length", "stop"), ) - def test_llm_span_finish_reasons_deduplicated_from_output_messages(self): - invocation = LLMInvocation( - request_model="model-output-dedup", - provider="test-provider", + def test_llm_span_finish_reasons_from_output_messages(self): + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="model-output-reasons" ) - - self.telemetry_handler.start_llm(invocation) assert invocation.span is not None invocation.output_messages = [ _create_output_message("response-1", finish_reason="stop"), _create_output_message("response-2", finish_reason="length"), _create_output_message("response-3", finish_reason="stop"), ] - self.telemetry_handler.stop_llm(invocation) + invocation.stop() span = _get_single_span(self.span_exporter) attrs = _get_span_attributes(span) self.assertEqual( attrs[GenAI.GEN_AI_RESPONSE_FINISH_REASONS], - ("length", "stop"), + ("stop", "length", "stop"), ) def test_llm_span_uses_expected_schema_url(self): - invocation = LLMInvocation( - request_model="schema-model", - provider="schema-provider", + invocation = self.telemetry_handler.start_inference( + "schema-provider", request_model="schema-model" ) - - self.telemetry_handler.start_llm(invocation) assert invocation.span is not None - self.telemetry_handler.stop_llm(invocation) + invocation.stop() span = _get_single_span(self.span_exporter) instrumentation = getattr(span, "instrumentation_scope", None) @@ -457,6 +438,24 @@ def test_llm_span_uses_expected_schema_url(self): == Schemas.V1_37_0.value ) + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="EVENT_ONLY", + emit_event="true", + ) + def test_llm_log_uses_expected_schema_url(self): + invocation = self.telemetry_handler.start_inference( + "schema-provider", request_model="schema-model" + ) + invocation.output_messages = [_create_output_message()] + invocation.stop() + + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 1) + self.assertEqual( + logs[0].instrumentation_scope.schema_url, Schemas.V1_37_0.value + ) + @patch_env_vars( stability_mode="gen_ai_latest_experimental", content_capturing="SPAN_ONLY", @@ -466,20 +465,14 @@ def test_parent_child_span_relationship(self): message = _create_input_message("hi") chat_generation = _create_output_message("ok") - with self.telemetry_handler.llm() as parent_invocation: - for attr, value in { - "request_model": "parent-model", - "input_messages": [message], - "provider": "test-provider", - }.items(): - setattr(parent_invocation, attr, value) - with self.telemetry_handler.llm() as child_invocation: - for attr, value in { - "request_model": "child-model", - "input_messages": [message], - "provider": "test-provider", - }.items(): - setattr(child_invocation, attr, value) + with self.telemetry_handler.inference( + "test-provider", request_model="parent-model" + ) as parent_invocation: + parent_invocation.input_messages = [message] + with self.telemetry_handler.inference( + "test-provider", request_model="child-model" + ) as child_invocation: + child_invocation.input_messages = [message] # Stop child first by exiting inner context child_invocation.output_messages = [chat_generation] # Then stop parent by exiting outer context @@ -500,6 +493,72 @@ def test_parent_child_span_relationship(self): # Parent should not have a parent (root) assert parent_span.parent is None + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="SPAN_ONLY", + emit_event="", + ) + def test_embedding_parent_child_span_relationship(self): + parent_invocation = self.telemetry_handler.start_embedding( + "test-provider", request_model="embed-parent-model" + ) + parent_invocation.input_tokens = 10 + assert parent_invocation.span is not None + child_invocation = self.telemetry_handler.start_embedding( + "test-provider", request_model="embed-child-model" + ) + child_invocation.input_tokens = 5 + assert child_invocation.span is not None + child_invocation.stop() + parent_invocation.stop() + + spans = self.span_exporter.get_finished_spans() + assert len(spans) == 2 + child_span = next( + s for s in spans if s.name == "embeddings embed-child-model" + ) + parent_span = next( + s for s in spans if s.name == "embeddings embed-parent-model" + ) + + assert child_span.context.trace_id == parent_span.context.trace_id + assert child_span.parent is not None + assert child_span.parent.span_id == parent_span.context.span_id + assert parent_span.parent is None + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="SPAN_ONLY", + emit_event="", + ) + def test_llm_parent_embedding_child_span_relationship(self): + message = _create_input_message("hi") + chat_generation = _create_output_message("ok") + + with self.telemetry_handler.inference( + "test-provider", request_model="parent-model" + ) as parent_invocation: + parent_invocation.input_messages = [message] + child_invocation = self.telemetry_handler.start_embedding( + "test-provider", request_model="embed-child-model" + ) + child_invocation.input_tokens = 3 + assert child_invocation.span is not None + child_invocation.stop() + parent_invocation.output_messages = [chat_generation] + + spans = self.span_exporter.get_finished_spans() + assert len(spans) == 2 + child_span = next( + s for s in spans if s.name == "embeddings embed-child-model" + ) + parent_span = next(s for s in spans if s.name == "chat parent-model") + + assert child_span.context.trace_id == parent_span.context.trace_id + assert child_span.parent is not None + assert child_span.parent.span_id == parent_span.context.span_id + assert parent_span.parent is None + def test_llm_context_manager_error_path_records_error_status_and_attrs( self, ): @@ -507,14 +566,12 @@ class BoomError(RuntimeError): pass message = _create_input_message("hi", role="user") - invocation = LLMInvocation( - request_model="test-model", - input_messages=[message], - provider="test-provider", - ) with self.assertRaises(BoomError): - with self.telemetry_handler.llm(invocation): + with self.telemetry_handler.inference( + "test-provider", request_model="test-model" + ) as invocation: + invocation.input_messages = [message] for attr, value in { "max_tokens": 128, "seed": 123, @@ -548,318 +605,104 @@ class BoomError(RuntimeError): }, ) - @patch_env_vars( - stability_mode="gen_ai_latest_experimental", - content_capturing="EVENT_ONLY", - emit_event="true", - ) - def test_emits_llm_event(self): - invocation = LLMInvocation( - request_model="event-model", - input_messages=[_create_input_message("test query")], - system_instruction=_create_system_instruction(), - provider="test-provider", - temperature=0.7, - max_tokens=100, - response_model_name="response-model", - response_id="event-response-id", - input_tokens=10, - output_tokens=20, - ) - - self.telemetry_handler.start_llm(invocation) - invocation.output_messages = [_create_output_message("test response")] - self.telemetry_handler.stop_llm(invocation) - - # Check that event was emitted - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 1) - log_record = logs[0].log_record - - # Verify event name - self.assertEqual( - log_record.event_name, "gen_ai.client.inference.operation.details" - ) - - # Verify event attributes - attrs = log_record.attributes - self.assertIsNotNone(attrs) - self.assertEqual(attrs[GenAI.GEN_AI_OPERATION_NAME], "chat") - self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_MODEL], "event-model") - self.assertEqual(attrs[GenAI.GEN_AI_PROVIDER_NAME], "test-provider") - self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_TEMPERATURE], 0.7) - self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_MAX_TOKENS], 100) - self.assertEqual(attrs[GenAI.GEN_AI_RESPONSE_MODEL], "response-model") - self.assertEqual(attrs[GenAI.GEN_AI_RESPONSE_ID], "event-response-id") - self.assertEqual(attrs[GenAI.GEN_AI_USAGE_INPUT_TOKENS], 10) - self.assertEqual(attrs[GenAI.GEN_AI_USAGE_OUTPUT_TOKENS], 20) - - # Verify messages are in structured format (not JSON string) - # OpenTelemetry may convert lists to tuples, so we normalize - input_msg = _normalize_to_dict( - _normalize_to_list(attrs[GenAI.GEN_AI_INPUT_MESSAGES])[0] - ) - self.assertEqual(input_msg["role"], "Human") - self.assertEqual( - _normalize_to_list(input_msg["parts"])[0]["content"], "test query" - ) - - output_msg = _normalize_to_dict( - _normalize_to_list(attrs[GenAI.GEN_AI_OUTPUT_MESSAGES])[0] - ) - self.assertEqual(output_msg["role"], "AI") - self.assertEqual( - _normalize_to_list(output_msg["parts"])[0]["content"], - "test response", - ) - self.assertEqual(output_msg["finish_reason"], "stop") - - # Verify system instruction is present in event in structured format - sys_instr = _normalize_to_dict( - _normalize_to_list(attrs[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS])[0] - ) - self.assertEqual(sys_instr["content"], "You are a helpful assistant.") - self.assertEqual(sys_instr["type"], "text") - - # Verify event context matches span context - span = _get_single_span(self.span_exporter) - self.assertIsNotNone(log_record.trace_id) - self.assertIsNotNone(log_record.span_id) - self.assertIsNotNone(span.context) - self.assertEqual(log_record.trace_id, span.context.trace_id) - self.assertEqual(log_record.span_id, span.context.span_id) - - @patch_env_vars( - stability_mode="gen_ai_latest_experimental", - content_capturing="SPAN_AND_EVENT", - emit_event="true", - ) - def test_emits_llm_event_and_span(self): - message = _create_input_message("combined test") - chat_generation = _create_output_message("combined response") - system_instruction = _create_system_instruction("System prompt here") - - invocation = LLMInvocation( - request_model="combined-model", - input_messages=[message], - system_instruction=system_instruction, - provider="test-provider", - ) - - self.telemetry_handler.start_llm(invocation) - invocation.output_messages = [chat_generation] - self.telemetry_handler.stop_llm(invocation) - - # Check span was created - span = _get_single_span(self.span_exporter) - span_attrs = _get_span_attributes(span) - self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, span_attrs) - - # Check event was emitted - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 1) - log_record = logs[0].log_record - self.assertEqual( - log_record.event_name, "gen_ai.client.inference.operation.details" - ) - self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, log_record.attributes) - # Verify system instruction in both span and event - self.assertIn(GenAI.GEN_AI_SYSTEM_INSTRUCTIONS, span_attrs) - span_system = json.loads(span_attrs[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS]) - self.assertEqual(span_system[0]["content"], "System prompt here") - event_attrs = log_record.attributes - self.assertIn(GenAI.GEN_AI_SYSTEM_INSTRUCTIONS, event_attrs) - event_system = event_attrs[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS] - event_system_list = ( - list(event_system) - if isinstance(event_system, tuple) - else event_system - ) - event_sys_instr = ( - dict(event_system_list[0]) - if isinstance(event_system_list[0], tuple) - else event_system_list[0] - ) - self.assertEqual(event_sys_instr["content"], "System prompt here") - # Verify event context matches span context - span = _get_single_span(self.span_exporter) - self.assertIsNotNone(log_record.trace_id) - self.assertIsNotNone(log_record.span_id) - self.assertIsNotNone(span.context) - self.assertEqual(log_record.trace_id, span.context.trace_id) - self.assertEqual(log_record.span_id, span.context.span_id) - - @patch_env_vars( - stability_mode="gen_ai_latest_experimental", - content_capturing="EVENT_ONLY", - emit_event="true", - ) - def test_emits_llm_event_with_error(self): - class TestError(RuntimeError): + def test_embedding_context_manager_error_path_records_error_status_and_attrs( + self, + ): + class BoomError(RuntimeError): pass - message = _create_input_message("error test") - invocation = LLMInvocation( - request_model="error-model", - input_messages=[message], - provider="test-provider", - ) - - self.telemetry_handler.start_llm(invocation) - error = Error(message="Test error occurred", type=TestError) - self.telemetry_handler.fail_llm(invocation, error) - - # Check event was emitted - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 1) - log_record = logs[0].log_record - attrs = log_record.attributes + with self.assertRaises(BoomError): + with self.telemetry_handler.embedding( + "test-provider", + request_model="embed-model", + server_address="embed.example.com", + server_port=443, + ) as invocation: + invocation.dimension_count = 1536 + invocation.input_tokens = 7 + invocation.attributes["custom_embed_attr"] = "value" + invocation.response_model_name = "embed-response-model" + raise BoomError("embedding boom") - # Verify error attribute is present - self.assertEqual( - attrs[error_attributes.ERROR_TYPE], TestError.__qualname__ - ) - self.assertEqual(attrs[GenAI.GEN_AI_OPERATION_NAME], "chat") - self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_MODEL], "error-model") - # Verify event context matches span context span = _get_single_span(self.span_exporter) - self.assertIsNotNone(log_record.trace_id) - self.assertIsNotNone(log_record.span_id) - self.assertIsNotNone(span.context) - self.assertEqual(log_record.trace_id, span.context.trace_id) - self.assertEqual(log_record.span_id, span.context.span_id) - - @patch_env_vars( - stability_mode="gen_ai_latest_experimental", - content_capturing="EVENT_ONLY", - emit_event="false", - ) - def test_does_not_emit_llm_event_when_emit_event_false(self): - message = _create_input_message("emit false test") - chat_generation = _create_output_message("emit false response") - - invocation = LLMInvocation( - request_model="emit-false-model", - input_messages=[message], - provider="test-provider", + assert span.status.status_code == StatusCode.ERROR + _assert_span_time_order(span) + span_attrs = _get_span_attributes(span) + _assert_span_attributes( + span_attrs, + { + GenAI.GEN_AI_OPERATION_NAME: "embeddings", + GenAI.GEN_AI_REQUEST_MODEL: "embed-model", + GenAI.GEN_AI_PROVIDER_NAME: "test-provider", + GenAI.GEN_AI_EMBEDDINGS_DIMENSION_COUNT: 1536, + GenAI.GEN_AI_USAGE_INPUT_TOKENS: 7, + GenAI.GEN_AI_RESPONSE_MODEL: "embed-response-model", + server_attributes.SERVER_ADDRESS: "embed.example.com", + server_attributes.SERVER_PORT: 443, + "custom_embed_attr": "value", + error_attributes.ERROR_TYPE: BoomError.__qualname__, + }, ) - self.telemetry_handler.start_llm(invocation) - invocation.output_messages = [chat_generation] - self.telemetry_handler.stop_llm(invocation) - - # Check no event was emitted - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 0) - - @patch_env_vars( - stability_mode="gen_ai_latest_experimental", - content_capturing="NO_CONTENT", - emit_event="", - ) - def test_does_not_emit_llm_event_by_default_for_no_content(self): - """Test that event is not emitted by default when content_capturing is NO_CONTENT and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" - invocation = LLMInvocation( - request_model="default-model", - input_messages=[_create_input_message("default test")], - provider="test-provider", - ) - - self.telemetry_handler.start_llm(invocation) - invocation.output_messages = [ - _create_output_message("default response") - ] - self.telemetry_handler.stop_llm(invocation) - - # Check that no event was emitted (NO_CONTENT defaults to False) - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 0) - @patch_env_vars( stability_mode="gen_ai_latest_experimental", content_capturing="SPAN_ONLY", emit_event="", ) - def test_does_not_emit_llm_event_by_default_for_span_only(self): - """Test that event is not emitted by default when content_capturing is SPAN_ONLY and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" - invocation = LLMInvocation( - request_model="default-model", - input_messages=[_create_input_message("default test")], - provider="test-provider", - ) - - self.telemetry_handler.start_llm(invocation) - invocation.output_messages = [ - _create_output_message("default response") - ] - self.telemetry_handler.stop_llm(invocation) + def test_embedding_manual_start_and_stop_creates_span(self): + invocation = self.telemetry_handler.start_embedding( + "test-provider", + request_model="embed-model", + server_address="custom.server.com", + server_port=42, + ) + invocation.dimension_count = 1536 + invocation.encoding_formats = ["float"] + invocation.input_tokens = 123 + invocation.attributes["custom_embed_attr"] = "value" + assert invocation.span is not None + invocation.attributes.update({"extra_embed": "info"}) + invocation.metric_attributes = {"should not be on span": "value"} + invocation.stop() - # Check that no event was emitted (SPAN_ONLY defaults to False) - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 0) + span = _get_single_span(self.span_exporter) + self.assertEqual(span.name, "embeddings embed-model") + self.assertEqual(span.kind, trace.SpanKind.CLIENT) + _assert_span_time_order(span) - @patch_env_vars( - stability_mode="gen_ai_latest_experimental", - content_capturing="EVENT_ONLY", - emit_event="", - ) - def test_emits_llm_event_by_default_for_event_only(self): - """Test that event is emitted by default when content_capturing is EVENT_ONLY and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" - invocation = LLMInvocation( - request_model="default-model", - input_messages=[_create_input_message("default test")], - provider="test-provider", + attrs = _get_span_attributes(span) + _assert_span_attributes( + attrs, + { + GenAI.GEN_AI_OPERATION_NAME: "embeddings", + GenAI.GEN_AI_REQUEST_MODEL: "embed-model", + GenAI.GEN_AI_PROVIDER_NAME: "test-provider", + GenAI.GEN_AI_EMBEDDINGS_DIMENSION_COUNT: 1536, + GenAI.GEN_AI_REQUEST_ENCODING_FORMATS: ("float",), + GenAI.GEN_AI_USAGE_INPUT_TOKENS: 123, + server_attributes.SERVER_ADDRESS: "custom.server.com", + server_attributes.SERVER_PORT: 42, + "custom_embed_attr": "value", + "extra_embed": "info", + }, ) - self.telemetry_handler.start_llm(invocation) - invocation.output_messages = [ - _create_output_message("default response") - ] - self.telemetry_handler.stop_llm(invocation) - - # Check that event was emitted (EVENT_ONLY defaults to True) - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 1) - log_record = logs[0].log_record - self.assertEqual( - log_record.event_name, "gen_ai.client.inference.operation.details" - ) + def test_fail_with_exception_sets_error_status_and_type(self): + class BoomError(RuntimeError): + pass - @patch_env_vars( - stability_mode="gen_ai_latest_experimental", - content_capturing="SPAN_AND_EVENT", - emit_event="", - ) - def test_emits_llm_event_by_default_for_span_and_event(self): - """Test that event is emitted by default when content_capturing is SPAN_AND_EVENT and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" - message = _create_input_message("span and event test") - chat_generation = _create_output_message("span and event response") - system_instruction = _create_system_instruction("System prompt") - - invocation = LLMInvocation( - request_model="span-and-event-model", - input_messages=[message], - system_instruction=system_instruction, - provider="test-provider", + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="test-model" ) + invocation.fail(BoomError("boom")) - self.telemetry_handler.start_llm(invocation) - invocation.output_messages = [chat_generation] - self.telemetry_handler.stop_llm(invocation) - - # Check span was created span = _get_single_span(self.span_exporter) - span_attrs = _get_span_attributes(span) - self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, span_attrs) - - # Check that event was emitted (SPAN_AND_EVENT defaults to True) - logs = self.log_exporter.get_finished_logs() - self.assertEqual(len(logs), 1) - log_record = logs[0].log_record - self.assertEqual( - log_record.event_name, "gen_ai.client.inference.operation.details" + assert span.status.status_code == StatusCode.ERROR + assert span.status.description == "boom" + assert ( + _get_span_attributes(span)[error_attributes.ERROR_TYPE] + == BoomError.__qualname__ ) - self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, log_record.attributes) class AnyNonNone: diff --git a/util/opentelemetry-util-genai/tests/test_utils_events.py b/util/opentelemetry-util-genai/tests/test_utils_events.py new file mode 100644 index 0000000000..7febea44c6 --- /dev/null +++ b/util/opentelemetry-util-genai/tests/test_utils_events.py @@ -0,0 +1,356 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import unittest + +from opentelemetry.sdk._logs import LoggerProvider +from opentelemetry.sdk._logs.export import ( + InMemoryLogExporter, + SimpleLogRecordProcessor, +) +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, +) +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAI, +) +from opentelemetry.semconv.attributes import error_attributes +from opentelemetry.util.genai.handler import get_telemetry_handler +from opentelemetry.util.genai.types import Error + +from .test_utils import ( + _create_input_message, + _create_output_message, + _create_system_instruction, + _get_single_span, + _get_span_attributes, + _normalize_to_dict, + _normalize_to_list, + patch_env_vars, +) + + +class TestTelemetryHandlerEvents(unittest.TestCase): + def setUp(self): + self.span_exporter = InMemorySpanExporter() + tracer_provider = TracerProvider() + tracer_provider.add_span_processor( + SimpleSpanProcessor(self.span_exporter) + ) + self.log_exporter = InMemoryLogExporter() + logger_provider = LoggerProvider() + logger_provider.add_log_record_processor( + SimpleLogRecordProcessor(self.log_exporter) + ) + self.telemetry_handler = get_telemetry_handler( + tracer_provider=tracer_provider, logger_provider=logger_provider + ) + + def tearDown(self): + self.span_exporter.clear() + self.log_exporter.clear() + if hasattr(get_telemetry_handler, "_default_handler"): + delattr(get_telemetry_handler, "_default_handler") + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="EVENT_ONLY", + emit_event="true", + ) + def test_emits_llm_event(self): + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="event-model" + ) + invocation.input_messages = [_create_input_message("test query")] + invocation.system_instruction = _create_system_instruction() + invocation.temperature = 0.7 + invocation.max_tokens = 100 + invocation.response_model_name = "response-model" + invocation.response_id = "event-response-id" + invocation.input_tokens = 10 + invocation.output_tokens = 20 + invocation.output_messages = [_create_output_message("test response")] + invocation.stop() + + # Check that event was emitted + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 1) + log_record = logs[0].log_record + + # Verify event name + self.assertEqual( + log_record.event_name, "gen_ai.client.inference.operation.details" + ) + + # Verify event attributes + attrs = log_record.attributes + self.assertIsNotNone(attrs) + self.assertEqual(attrs[GenAI.GEN_AI_OPERATION_NAME], "chat") + self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_MODEL], "event-model") + self.assertEqual(attrs[GenAI.GEN_AI_PROVIDER_NAME], "test-provider") + self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_TEMPERATURE], 0.7) + self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_MAX_TOKENS], 100) + self.assertEqual(attrs[GenAI.GEN_AI_RESPONSE_MODEL], "response-model") + self.assertEqual(attrs[GenAI.GEN_AI_RESPONSE_ID], "event-response-id") + self.assertEqual(attrs[GenAI.GEN_AI_USAGE_INPUT_TOKENS], 10) + self.assertEqual(attrs[GenAI.GEN_AI_USAGE_OUTPUT_TOKENS], 20) + + # Verify messages are in structured format (not JSON string) + # OpenTelemetry may convert lists to tuples, so we normalize + input_msg = _normalize_to_dict( + _normalize_to_list(attrs[GenAI.GEN_AI_INPUT_MESSAGES])[0] + ) + self.assertEqual(input_msg["role"], "Human") + self.assertEqual( + _normalize_to_list(input_msg["parts"])[0]["content"], "test query" + ) + + output_msg = _normalize_to_dict( + _normalize_to_list(attrs[GenAI.GEN_AI_OUTPUT_MESSAGES])[0] + ) + self.assertEqual(output_msg["role"], "AI") + self.assertEqual( + _normalize_to_list(output_msg["parts"])[0]["content"], + "test response", + ) + self.assertEqual(output_msg["finish_reason"], "stop") + + # Verify system instruction is present in event in structured format + sys_instr = _normalize_to_dict( + _normalize_to_list(attrs[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS])[0] + ) + self.assertEqual(sys_instr["content"], "You are a helpful assistant.") + self.assertEqual(sys_instr["type"], "text") + + # Verify event context matches span context + span = _get_single_span(self.span_exporter) + self.assertIsNotNone(log_record.trace_id) + self.assertIsNotNone(log_record.span_id) + self.assertIsNotNone(span.context) + self.assertEqual(log_record.trace_id, span.context.trace_id) + self.assertEqual(log_record.span_id, span.context.span_id) + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="SPAN_AND_EVENT", + emit_event="true", + ) + def test_emits_llm_event_and_span(self): + message = _create_input_message("combined test") + chat_generation = _create_output_message("combined response") + system_instruction = _create_system_instruction("System prompt here") + + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="combined-model" + ) + invocation.input_messages = [message] + invocation.system_instruction = system_instruction + invocation.output_messages = [chat_generation] + invocation.stop() + + # Check span was created + span = _get_single_span(self.span_exporter) + span_attrs = _get_span_attributes(span) + self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, span_attrs) + + # Check event was emitted + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 1) + log_record = logs[0].log_record + self.assertEqual( + log_record.event_name, "gen_ai.client.inference.operation.details" + ) + self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, log_record.attributes) + # Verify system instruction in both span and event + self.assertIn(GenAI.GEN_AI_SYSTEM_INSTRUCTIONS, span_attrs) + span_system = json.loads(span_attrs[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS]) + self.assertEqual(span_system[0]["content"], "System prompt here") + event_attrs = log_record.attributes + self.assertIn(GenAI.GEN_AI_SYSTEM_INSTRUCTIONS, event_attrs) + event_system = event_attrs[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS] + event_system_list = ( + list(event_system) + if isinstance(event_system, tuple) + else event_system + ) + event_sys_instr = ( + dict(event_system_list[0]) + if isinstance(event_system_list[0], tuple) + else event_system_list[0] + ) + self.assertEqual(event_sys_instr["content"], "System prompt here") + # Verify event context matches span context + span = _get_single_span(self.span_exporter) + self.assertIsNotNone(log_record.trace_id) + self.assertIsNotNone(log_record.span_id) + self.assertIsNotNone(span.context) + self.assertEqual(log_record.trace_id, span.context.trace_id) + self.assertEqual(log_record.span_id, span.context.span_id) + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="EVENT_ONLY", + emit_event="true", + ) + def test_emits_llm_event_with_error(self): + class TestError(RuntimeError): + pass + + message = _create_input_message("error test") + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="error-model" + ) + invocation.input_messages = [message] + error = Error(message="Test error occurred", type=TestError) + invocation.fail(error) + + # Check event was emitted + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 1) + log_record = logs[0].log_record + attrs = log_record.attributes + + # Verify error attribute is present + self.assertEqual( + attrs[error_attributes.ERROR_TYPE], TestError.__qualname__ + ) + self.assertEqual(attrs[GenAI.GEN_AI_OPERATION_NAME], "chat") + self.assertEqual(attrs[GenAI.GEN_AI_REQUEST_MODEL], "error-model") + # Verify event context matches span context + span = _get_single_span(self.span_exporter) + self.assertIsNotNone(log_record.trace_id) + self.assertIsNotNone(log_record.span_id) + self.assertIsNotNone(span.context) + self.assertEqual(log_record.trace_id, span.context.trace_id) + self.assertEqual(log_record.span_id, span.context.span_id) + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="EVENT_ONLY", + emit_event="false", + ) + def test_does_not_emit_llm_event_when_emit_event_false(self): + message = _create_input_message("emit false test") + chat_generation = _create_output_message("emit false response") + + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="emit-false-model" + ) + invocation.input_messages = [message] + invocation.output_messages = [chat_generation] + invocation.stop() + + # Check no event was emitted + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 0) + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="NO_CONTENT", + emit_event="", + ) + def test_does_not_emit_llm_event_by_default_for_no_content(self): + """Test that event is not emitted by default when content_capturing is NO_CONTENT and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="default-model" + ) + invocation.input_messages = [_create_input_message("default test")] + invocation.output_messages = [ + _create_output_message("default response") + ] + invocation.stop() + + # Check that no event was emitted (NO_CONTENT defaults to False) + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 0) + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="SPAN_ONLY", + emit_event="", + ) + def test_does_not_emit_llm_event_by_default_for_span_only(self): + """Test that event is not emitted by default when content_capturing is SPAN_ONLY and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="default-model" + ) + invocation.input_messages = [_create_input_message("default test")] + invocation.output_messages = [ + _create_output_message("default response") + ] + invocation.stop() + + # Check that no event was emitted (SPAN_ONLY defaults to False) + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 0) + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="EVENT_ONLY", + emit_event="", + ) + def test_emits_llm_event_by_default_for_event_only(self): + """Test that event is emitted by default when content_capturing is EVENT_ONLY and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="default-model" + ) + invocation.input_messages = [_create_input_message("default test")] + invocation.output_messages = [ + _create_output_message("default response") + ] + invocation.stop() + + # Check that event was emitted (EVENT_ONLY defaults to True) + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 1) + log_record = logs[0].log_record + self.assertEqual( + log_record.event_name, "gen_ai.client.inference.operation.details" + ) + + @patch_env_vars( + stability_mode="gen_ai_latest_experimental", + content_capturing="SPAN_AND_EVENT", + emit_event="", + ) + def test_emits_llm_event_by_default_for_span_and_event(self): + """Test that event is emitted by default when content_capturing is SPAN_AND_EVENT and OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT is not set.""" + message = _create_input_message("span and event test") + chat_generation = _create_output_message("span and event response") + system_instruction = _create_system_instruction("System prompt") + + invocation = self.telemetry_handler.start_inference( + "test-provider", request_model="span-and-event-model" + ) + invocation.input_messages = [message] + invocation.system_instruction = system_instruction + invocation.output_messages = [chat_generation] + invocation.stop() + + # Check span was created + span = _get_single_span(self.span_exporter) + span_attrs = _get_span_attributes(span) + self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, span_attrs) + + # Check that event was emitted (SPAN_AND_EVENT defaults to True) + logs = self.log_exporter.get_finished_logs() + self.assertEqual(len(logs), 1) + log_record = logs[0].log_record + self.assertEqual( + log_record.event_name, "gen_ai.client.inference.operation.details" + ) + self.assertIn(GenAI.GEN_AI_INPUT_MESSAGES, log_record.attributes) diff --git a/util/opentelemetry-util-genai/tests/test_workflow_invocation.py b/util/opentelemetry-util-genai/tests/test_workflow_invocation.py new file mode 100644 index 0000000000..3a80f1c214 --- /dev/null +++ b/util/opentelemetry-util-genai/tests/test_workflow_invocation.py @@ -0,0 +1,100 @@ +import unittest + +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, +) +from opentelemetry.trace import INVALID_SPAN +from opentelemetry.util.genai.handler import TelemetryHandler +from opentelemetry.util.genai.types import ( + InputMessage, + OutputMessage, + Text, +) + + +class TestWorkflowInvocation(unittest.TestCase): + def setUp(self): + self.span_exporter = InMemorySpanExporter() + tracer_provider = TracerProvider() + tracer_provider.add_span_processor( + SimpleSpanProcessor(self.span_exporter) + ) + self.handler = TelemetryHandler(tracer_provider=tracer_provider) + + def test_default_values(self): + invocation = self.handler.start_workflow(name=None) + invocation.stop() + assert invocation.name is None + assert invocation._operation_name == "invoke_workflow" + assert not invocation.input_messages + assert not invocation.output_messages + assert invocation.span is not INVALID_SPAN + assert not invocation.attributes + + def test_custom_name(self): + invocation = self.handler.start_workflow( + name="customer_support_pipeline" + ) + invocation.stop() + assert invocation.name == "customer_support_pipeline" + + def test_with_input_messages(self): + msg = InputMessage(role="user", parts=[Text(content="hello")]) + invocation = self.handler.start_workflow(name="test") + invocation.input_messages = [msg] + invocation.stop() + assert len(invocation.input_messages) == 1 + assert invocation.input_messages[0].role == "user" + + def test_with_output_messages(self): + msg = OutputMessage( + role="assistant", parts=[Text(content="hi")], finish_reason="stop" + ) + invocation = self.handler.start_workflow(name="test") + invocation.output_messages = [msg] + invocation.stop() + assert len(invocation.output_messages) == 1 + assert invocation.output_messages[0].finish_reason == "stop" + + def test_inherits_genai_invocation(self): + invocation = self.handler.start_workflow(name="test") + invocation.attributes["key"] = "value" + invocation.stop() + spans = self.span_exporter.get_finished_spans() + assert spans[0].attributes is not None + assert spans[0].attributes["key"] == "value" + + def test_default_lists_are_independent(self): + """Ensure separate invocations get separate list instances.""" + inv1 = self.handler.start_workflow(name=None) + inv2 = self.handler.start_workflow(name=None) + inv1.input_messages.append(InputMessage(role="user", parts=[])) + assert len(inv2.input_messages) == 0 + inv1.stop() + inv2.stop() + + def test_default_attributes_are_independent(self): + inv1 = self.handler.start_workflow(name=None) + inv2 = self.handler.start_workflow(name=None) + inv1.attributes["foo"] = "bar" + assert "foo" not in inv2.attributes + inv1.stop() + inv2.stop() + + def test_full_construction(self): + inp = InputMessage(role="user", parts=[Text(content="query")]) + out = OutputMessage( + role="assistant", + parts=[Text(content="answer")], + finish_reason="stop", + ) + invocation = self.handler.start_workflow(name="my_workflow") + invocation.input_messages = [inp] + invocation.output_messages = [out] + invocation.stop() + assert invocation.name == "my_workflow" + assert len(invocation.input_messages) == 1 + assert len(invocation.output_messages) == 1 + assert invocation.output_messages[0].parts[0].content == "answer" diff --git a/util/opentelemetry-util-http/pyproject.toml b/util/opentelemetry-util-http/pyproject.toml index 87d963cf01..48170a2f31 100644 --- a/util/opentelemetry-util-http/pyproject.toml +++ b/util/opentelemetry-util-http/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Web util for OpenTelemetry" readme = "README.rst" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, ] @@ -18,7 +18,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/httplib.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/httplib.py index c969710cd3..2a3723c927 100644 --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/httplib.py +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/httplib.py @@ -95,6 +95,7 @@ def trysetip( return True sock = "" + ip = None try: sock: typing.Optional[socket.socket] = conn.sock logger.debug("Got socket: %s", sock) @@ -112,8 +113,9 @@ def trysetip( stack_info=True, ) else: - for span in spanlist: - span.set_attribute(NET_PEER_IP, ip) + if ip is not None: + for span in spanlist: + span.set_attribute(NET_PEER_IP, ip) return True diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/version.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/version.py index ed89ddd1cc..a07bc2663e 100644 --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/version.py +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.62b0.dev" +__version__ = "0.63b0.dev" diff --git a/util/opentelemetry-util-http/test-requirements.txt b/util/opentelemetry-util-http/test-requirements.txt index ea2448bab9..01a7bff69a 100644 --- a/util/opentelemetry-util-http/test-requirements.txt +++ b/util/opentelemetry-util-http/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 Deprecated==1.2.14 iniconfig==2.0.0 packaging==24.0 -pluggy==1.5.0 +pluggy==1.6.0 py-cpuinfo==9.0.0 pytest==7.4.4 tomli==2.0.1 diff --git a/uv.lock b/uv.lock index 6b2e6e21ed..14d5ea35b6 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,7 @@ resolution-markers = [ [manifest] members = [ + "opentelemetry-distro", "opentelemetry-exporter-credential-provider-gcp", "opentelemetry-exporter-prometheus-remote-write", "opentelemetry-exporter-richconsole", @@ -26,7 +27,6 @@ members = [ "opentelemetry-instrumentation-asyncio", "opentelemetry-instrumentation-asyncpg", "opentelemetry-instrumentation-aws-lambda", - "opentelemetry-instrumentation-boto", "opentelemetry-instrumentation-boto3sqs", "opentelemetry-instrumentation-botocore", "opentelemetry-instrumentation-cassandra", @@ -95,6 +95,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/91/513971861d845d28160ecb205ae2cfaf618b16918a9cd4e0b832b5360ce7/aio_pika-9.5.8-py3-none-any.whl", hash = "sha256:f4c6cb8a6c5176d00f39fd7431e9702e638449bc6e86d1769ad7548b2a506a8d", size = 54397, upload-time = "2025-11-12T10:37:08.374Z" }, ] +[[package]] +name = "aiobotocore" +version = "2.26.0" +source = { registry = "https://pypi.org/simple/" } +dependencies = [ + { name = "aiohttp" }, + { name = "aioitertools" }, + { name = "botocore" }, + { name = "jmespath" }, + { name = "multidict" }, + { name = "python-dateutil" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/f8/99fa90d9c25b78292899fd4946fce97b6353838b5ecc139ad8ba1436e70c/aiobotocore-2.26.0.tar.gz", hash = "sha256:50567feaf8dfe2b653570b4491f5bc8c6e7fb9622479d66442462c021db4fadc", size = 122026, upload-time = "2025-11-28T07:54:59.956Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/58/3bf0b7d474607dc7fd67dd1365c4e0f392c8177eaf4054e5ddee3ebd53b5/aiobotocore-2.26.0-py3-none-any.whl", hash = "sha256:a793db51c07930513b74ea7a95bd79aaa42f545bdb0f011779646eafa216abec", size = 87333, upload-time = "2025-11-28T07:54:58.457Z" }, +] + [[package]] name = "aiohappyeyeballs" version = "2.6.1" @@ -224,6 +242,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, ] +[[package]] +name = "aioitertools" +version = "0.13.0" +source = { registry = "https://pypi.org/simple/" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322, upload-time = "2025-11-06T22:17:07.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182, upload-time = "2025-11-06T22:17:06.502Z" }, +] + [[package]] name = "aiokafka" version = "0.13.0" @@ -530,41 +557,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, ] -[[package]] -name = "boto" -version = "2.49.0" -source = { registry = "https://pypi.org/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/af/54a920ff4255664f5d238b5aebd8eedf7a07c7a5e71e27afcfe840b82f51/boto-2.49.0.tar.gz", hash = "sha256:ea0d3b40a2d852767be77ca343b58a9e3a4b00d9db440efb8da74b4e58025e5a", size = 1478498, upload-time = "2018-07-11T20:58:58.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/10/c0b78c27298029e4454a472a1919bde20cb182dab1662cec7f2ca1dcc523/boto-2.49.0-py2.py3-none-any.whl", hash = "sha256:147758d41ae7240dc989f0039f27da8ca0d53734be0eb869ef16e3adcfa462e8", size = 1359202, upload-time = "2018-07-11T20:58:55.711Z" }, -] - [[package]] name = "boto3" -version = "1.42.53" +version = "1.41.5" source = { registry = "https://pypi.org/simple/" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/ef/03460914019db52301a6084460f0dd738f3f9e89d2ddf5bd33cef8168e63/boto3-1.42.53.tar.gz", hash = "sha256:56bc79388763995852b6d3fe48023e661e63fc2e60a921273c422d0171b9fbfb", size = 112812, upload-time = "2026-02-19T20:33:58.422Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/81/450cd4143864959264a3d80f9246175a20de8c1e50ec889c710eaa28cdd9/boto3-1.41.5.tar.gz", hash = "sha256:bc7806bee681dfdff2fe2b74967b107a56274f1e66ebe4d20dc8eee1ea408d17", size = 111594, upload-time = "2025-11-26T20:27:47.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/ea/08dfba25a5822a7254b20aa905a9937177ca1532dd7f47c926875dd87299/boto3-1.42.53-py3-none-any.whl", hash = "sha256:3bd32f3508a6e9851671d0ef3b1f9e8ee7e8c095aa0488bcd9e86074aef5b7eb", size = 140555, upload-time = "2026-02-19T20:33:55.691Z" }, + { url = "https://files.pythonhosted.org/packages/3c/56/f47a80254ed4991cce9a2f6d8ae8aafbc8df1c3270e966b2927289e5a12f/boto3-1.41.5-py3-none-any.whl", hash = "sha256:bb278111bfb4c33dca8342bda49c9db7685e43debbfa00cc2a5eb854dd54b745", size = 139344, upload-time = "2025-11-26T20:27:45.571Z" }, ] [[package]] name = "botocore" -version = "1.42.53" +version = "1.41.5" source = { registry = "https://pypi.org/simple/" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/b6/0b2ab38e422e93f28b7a394a29881a9d767b79831fa1957a3ccab996a70e/botocore-1.42.53.tar.gz", hash = "sha256:0bc1a2e1b6ae4c8397c9bede3bb9007b4f16e159ef2ca7f24837e31d5860caac", size = 14918644, upload-time = "2026-02-19T20:33:44.814Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/22/7fe08c726a2e3b11a0aef8bf177e83891c9cb2dc1809d35c9ed91a9e60e6/botocore-1.41.5.tar.gz", hash = "sha256:0367622b811597d183bfcaab4a350f0d3ede712031ce792ef183cabdee80d3bf", size = 14668152, upload-time = "2025-11-26T20:27:38.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/dc/cf3b2ec4a419b20d2cd6ba8e1961bc59b7ec9801339628e31551dac23801/botocore-1.42.53-py3-none-any.whl", hash = "sha256:1255db56bc0a284a8caa182c20966277e6c8871b6881cf816d40e993fa5da503", size = 14589472, upload-time = "2026-02-19T20:33:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/4e/21cd0b8f365449f1576f93de1ec8718ed18a7a3bc086dfbdeb79437bba7a/botocore-1.41.5-py3-none-any.whl", hash = "sha256:3fef7fcda30c82c27202d232cfdbd6782cb27f20f8e7e21b20606483e66ee73a", size = 14337008, upload-time = "2025-11-26T20:27:35.208Z" }, ] [[package]] @@ -907,39 +925,35 @@ wheels = [ [[package]] name = "confluent-kafka" -version = "2.13.0" -source = { registry = "https://pypi.org/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/b4/d0/1f5055331fa660225de6829b143e6f083913f0a96481134a91390bad62c1/confluent_kafka-2.13.0.tar.gz", hash = "sha256:eff7a4391a9e6d4a33f0c05d0935b200a7463834f1f5d6e6253be318f910babd", size = 273621, upload-time = "2026-01-05T10:25:08.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/75/b3ac0e7fc74a577eb58872f07faef3d1d50a1e1ac7d105b167d330105bc5/confluent_kafka-2.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:264f86956d3bbd26bc14fd2d4060908a0c53d26b14ff33d21fa83369f55d8d3b", size = 3626152, upload-time = "2026-01-05T10:23:21.034Z" }, - { url = "https://files.pythonhosted.org/packages/17/7c/897913554793d43fcf69e0bfa1ba2b647e007f45fa43b190645e5b5f6518/confluent_kafka-2.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4af5f129acce30110c7a1c9c05c54821c1283850e38c67715bed7c3c8df322c6", size = 3186880, upload-time = "2026-01-05T10:23:24.382Z" }, - { url = "https://files.pythonhosted.org/packages/27/7c/c956373d15a57290b9d00c5e2c1a5fb65b8d4f77fa64535ad7370d9ed750/confluent_kafka-2.13.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:9b975664e821d975c85ec9bba806b7c62eb9b23cf2ad3b41813862ee24caf00e", size = 3715940, upload-time = "2026-01-05T10:23:29.266Z" }, - { url = "https://files.pythonhosted.org/packages/10/92/66179579a1bbc91f2e700841ea698a99c8108580e46c2cd5b1812c707c38/confluent_kafka-2.13.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:fc633deedd3eba5c266bf12959d8c4173806d252db45d2c78d3bd9a874dc7ccf", size = 3973310, upload-time = "2026-01-05T10:23:31.775Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e6/66d3bd816d996058f9f46b46953e79b3ea2662f3538d0682a3f52f651393/confluent_kafka-2.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:2c0b19a83f519de8f2cb170bc7879b1d92ac342a75798722fbfe29965f21d5ad", size = 4093322, upload-time = "2026-01-05T10:23:34.235Z" }, - { url = "https://files.pythonhosted.org/packages/94/57/a90b9042a06bfbb8cc1e50142b8bcea29fde57a528dc10b188835e7c5c53/confluent_kafka-2.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:af19d6a2ab49f02cf699bc1dfb6f4bdcc3588e077c7b5319d73335b81fef93fd", size = 3625719, upload-time = "2026-01-05T10:23:36.469Z" }, - { url = "https://files.pythonhosted.org/packages/5a/ee/d0839c64de344de247d80b6f69a2d7f5d781bb55bdd1e98793549780a34d/confluent_kafka-2.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a406800c29e568e61ab687540391ac8eab210b79d24d7eb41b75b6117882e269", size = 3186456, upload-time = "2026-01-05T10:23:38.742Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e7/6224ab1c5dad5d70717f3e018f1d676be1d54ce3ea08b04b1d19e94484dc/confluent_kafka-2.13.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9bc38055f5a26bec15ca81b0465f90cb2663a31c19948d6421dc02090b4cbd4a", size = 3715598, upload-time = "2026-01-05T10:23:41.018Z" }, - { url = "https://files.pythonhosted.org/packages/64/88/a7a7ee4fdcca5340809482e6565cd3270fe82afd029a14cf921b51eef152/confluent_kafka-2.13.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7080a20f64293b1f81747748e6bda0d1e9a9d5d5f94cd1b0c625cfdf819f491f", size = 3972822, upload-time = "2026-01-05T10:23:44.029Z" }, - { url = "https://files.pythonhosted.org/packages/5f/46/2879be2a40b9b44a527a16c117c6a5f380fdd10d6735b43bce6fab6102d7/confluent_kafka-2.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:39fdfd4fa6371ebabf9e7be858ae68d4fa9a9fd72fd5fc3d739fdaa99997fd9a", size = 4093320, upload-time = "2026-01-05T10:23:46.347Z" }, - { url = "https://files.pythonhosted.org/packages/e9/cc/6bf9e5b3ee4bfdb39d3fcc7efabc9f577aa51e9e20139adc8d10e61593a5/confluent_kafka-2.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a69263f22a8c53c7d55067e7795ed49d22c374b9473df91982816a0448e6d242", size = 3631367, upload-time = "2026-01-05T10:23:48.076Z" }, - { url = "https://files.pythonhosted.org/packages/10/3d/c299df69885be6fdfc8b105b8106b2b4c73c745fa608cf579eb7e14a3da9/confluent_kafka-2.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0945c7f529e66a18aa19135aa18bfdc239ca6c0f6df6ca9b05a793d9b76c1c4b", size = 3191073, upload-time = "2026-01-05T10:23:49.967Z" }, - { url = "https://files.pythonhosted.org/packages/82/2e/854aedcd9c9042491c1fcafaadc03a3b0e357ddc23044781d02920b46ea2/confluent_kafka-2.13.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:321037a64c02acb13b5bde193b461c0514dca236a9f5236c847a3240313c297f", size = 3720530, upload-time = "2026-01-05T10:23:51.957Z" }, - { url = "https://files.pythonhosted.org/packages/1d/4a/210f0e1f8e77956ed1296c8b9bac2093413c1669ea0e923f590f2827aa1a/confluent_kafka-2.13.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d7a71ca8fd42d3eefa22eb202e7fc8a419e0fd4e3b59862918c21f4d1074f0c5", size = 3977296, upload-time = "2026-01-05T10:23:56.743Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bc/d51f48200bb7bec521289c0b70691ea73f91aa76529b1bff807bcbf89b12/confluent_kafka-2.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:37dddb1b92829b8862bc4fbce07789a79b73aa31eca413f3db187721a09975ff", size = 4093802, upload-time = "2026-01-05T10:23:58.703Z" }, - { url = "https://files.pythonhosted.org/packages/77/bd/c2ab440b4c4847a37b21e9623bbeaf17982ca45595ad62b8435a26d680f7/confluent_kafka-2.13.0-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:0af90b3c566786017a01693da0ec4a876ca14cf37bc6164872652a6cf2702453", size = 3195849, upload-time = "2026-01-05T10:24:00.854Z" }, - { url = "https://files.pythonhosted.org/packages/6a/40/a25a5895cf522bada81fc7ba6c0ad29219843206ede4e8d2138b7b095652/confluent_kafka-2.13.0-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:f25d05604dd92e9de72707582dded53aeb4737ef2e2c097a3ca08650200fc446", size = 3634806, upload-time = "2026-01-05T10:24:05.496Z" }, - { url = "https://files.pythonhosted.org/packages/fd/46/db30d27184ac8fb673ca469d576ef582def0b613a69456631734f7a5e267/confluent_kafka-2.13.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:74ddf5ec7fa6058221a619c850f44bdbe8d969d7ed6efe8abdc857d2e233df20", size = 3720848, upload-time = "2026-01-05T10:24:07.831Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f8/5b940a080ab71fc3c585839eae0c26341a749a7ebc001fca0276aff9df40/confluent_kafka-2.13.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:f8d1d00397f3f32a1bcf4604d4164bf75838bd009e1e28282a7ae25e16814ea4", size = 3977677, upload-time = "2026-01-05T10:24:09.988Z" }, - { url = "https://files.pythonhosted.org/packages/e1/cf/f9f979c08cfe1b8fd9203b329fc5c8c410e948f01272434bc1ce0c6334a5/confluent_kafka-2.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9d1fd035e2c47c4db5fe9b0f59a28fe2f2f1012887290dd0ea7d46741f686e99", size = 4153481, upload-time = "2026-01-05T10:24:12.167Z" }, - { url = "https://files.pythonhosted.org/packages/06/3f/c2120c002f85c5401d8ea29558acf041ad968b33cfca83916a7c0a740d53/confluent_kafka-2.13.0-cp314-cp314-macosx_13_0_arm64.whl", hash = "sha256:d448537147a33dd8c17656732989ddfe1d4a25a40bcb5f59bc63dc0a5041dd83", size = 3195696, upload-time = "2026-01-05T10:24:14.422Z" }, - { url = "https://files.pythonhosted.org/packages/35/b9/da4ef8fca4cbc76b6040a97a92085c09c0f23c42424989a2d68cec42c8d6/confluent_kafka-2.13.0-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:1325585f9fc283c32c30df4226178dc89cf43d00f5c240e1f77ddedc94573690", size = 3634632, upload-time = "2026-01-05T10:24:16.575Z" }, - { url = "https://files.pythonhosted.org/packages/09/ed/c7d5cc3c57aec126ffb12ffa5f4584acd264446a4117db3ad2dd69e47afe/confluent_kafka-2.13.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:266bbea18ce99f6e77ce0e9a118f353447c8705792ef5745eabcc5c6db08794a", size = 3720650, upload-time = "2026-01-05T10:24:18.657Z" }, - { url = "https://files.pythonhosted.org/packages/c3/2b/0a93b63a46b2ceaac3de92d494e32aab21bec398b40ac89108bbaa6894c3/confluent_kafka-2.13.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:7a373a1a3dd8e02dd218946583e951791480921fd777faeaf601c2834e2a6c0d", size = 3977364, upload-time = "2026-01-05T10:24:23.677Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/80ea6872421c4e30e033e035e5bdcf44c054993d5721623841c59b5f04c1/confluent_kafka-2.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:da956b2141d9f425dbfc3cf1c244ef6d0633b83fc5ceada6f496099258e63a68", size = 4271874, upload-time = "2026-01-05T10:24:26.265Z" }, - { url = "https://files.pythonhosted.org/packages/fc/1a/e6fe016bea63343010f485a1ecebfdc00d9b6e95ef6fe52a1d7793c0339c/confluent_kafka-2.13.0-cp314-cp314t-macosx_13_0_arm64.whl", hash = "sha256:fa354e9fb95e26545decd429477072ea3a98a2e7acac11d09156772e06f14680", size = 3194480, upload-time = "2026-01-05T10:24:28.515Z" }, - { url = "https://files.pythonhosted.org/packages/fb/54/c9668f214f2e736e51e2874053fe1ebcb8784425fefcfd6fd0d384dc5dcf/confluent_kafka-2.13.0-cp314-cp314t-macosx_13_0_x86_64.whl", hash = "sha256:e256dc3a993bf5fde0fa4a1b6a5b72e3521cedbc9dc4d7a65f159afa4b72e5b4", size = 3632866, upload-time = "2026-01-05T10:24:30.689Z" }, - { url = "https://files.pythonhosted.org/packages/13/f5/83e989962077003d91ba249158c7a1e21696604c301b3680fcbb427005d0/confluent_kafka-2.13.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:eb7988038b7f13ea490af93b9165ed40a3f385fb91e99ce8dacee8890e36e48a", size = 3718956, upload-time = "2026-01-05T10:24:33.035Z" }, - { url = "https://files.pythonhosted.org/packages/b8/84/1aaa5cfe1695f02d11e02ef39f180567780348b1f3e1161575f002a207fe/confluent_kafka-2.13.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:cfd8f011b0b0a109f8747312dba6cee45b39fb53fc7d53f28813bce84a91228d", size = 3976235, upload-time = "2026-01-05T10:24:35.122Z" }, +version = "2.13.2" +source = { registry = "https://pypi.org/simple/" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/38/f5855cae6d328fa66e689d068709f91cbbd4d72e7e03959998bd43ac6b26/confluent_kafka-2.13.2.tar.gz", hash = "sha256:619d10d1d77c9821ba913b3e42a33ade7f889f3573c7f3c17b57c3056e3310f5", size = 276068, upload-time = "2026-03-02T12:53:31.457Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/7c/bd4c2f49ecba57d3338b2f8b466530696eb65565480c18347539cc3104da/confluent_kafka-2.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4ee145e9b05fa56b3da104c3fda0cd63c22dd609ee6d8efea6e87a96ef4ad74", size = 3630069, upload-time = "2026-03-02T12:52:33.637Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f4/b6178a474f725c0aa389ad048c7eba91ea12eb0d392dab26f39d2cf95d60/confluent_kafka-2.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:478f11030a8ee7fafc816b4067f586222adbdc47d710f7e54eceb22a56320359", size = 3190436, upload-time = "2026-03-02T12:52:36.852Z" }, + { url = "https://files.pythonhosted.org/packages/48/6b/08c1efee551e513aa2e61d5583fe7d7bdf870372dfaf6ecf75ec96bdd7ff/confluent_kafka-2.13.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:92bf91c718911d7542aa6e1f60822fe9325b1a5173c5dd64b87c717246788ba2", size = 3719853, upload-time = "2026-03-02T12:52:38.549Z" }, + { url = "https://files.pythonhosted.org/packages/ba/67/98dfe8e699bfa6da0dbf9e03bd23c179ff0c9f27b88448a1d613de9a0d71/confluent_kafka-2.13.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8b4dd67cb7a973382e02e8ba26e33caf0c70910cf74ef077ca2ad99387fc154a", size = 3976926, upload-time = "2026-03-02T12:52:39.915Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/534803e8e1fd8246f66c651131c2c02b30c0c8018b3d6c04e9dd80abd792/confluent_kafka-2.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:e36f978d425deda2e57e832b3b9ac733c3668f2dae955b8953df8ae986e3cf5b", size = 4097308, upload-time = "2026-03-02T12:52:41.225Z" }, + { url = "https://files.pythonhosted.org/packages/71/a7/7dfee75b246f5e5f0832a27e365cd9e8050591c5f4301714672bea2375ce/confluent_kafka-2.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e85dc2aaf08dcac610d20b24d252a24891440cf33c09396c957781b8a1f24015", size = 3629660, upload-time = "2026-03-02T12:52:42.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/77/bc6bca93f455e91b41b196bb208b9cbfc517442a65abae2391f1af64cd2f/confluent_kafka-2.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eb1b218beeaae36b3fc94927e30df5f6d662858e766eada2369b290df0b1bff0", size = 3190013, upload-time = "2026-03-02T12:52:44.193Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ce/2ee04c1b2707b6dd7177eab40fced00b474671d2303e5096d96f3bf7e231/confluent_kafka-2.13.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69b4286296504b89c0c3cd1e531d12053c633e56d2c5b477ff9000524fe24eb5", size = 3719524, upload-time = "2026-03-02T12:52:45.488Z" }, + { url = "https://files.pythonhosted.org/packages/7c/93/5c40e2f7eae52774db6b14060254d001ae8c4ef8d4385bf2f13294dd929f/confluent_kafka-2.13.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e47be4267d3feda5bf1c066f140f61e61ea28bd6ecdb60c2a52ec1a91b8903e7", size = 3976453, upload-time = "2026-03-02T12:52:47.787Z" }, + { url = "https://files.pythonhosted.org/packages/46/85/a3d25b67470abbd4835fca714a419465323ba79dceefcdda65dfa4415c80/confluent_kafka-2.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:84dd6e7f456910aa4d4763d86efa0dded7167fdf1251b51d808dec0f124f5e13", size = 4097308, upload-time = "2026-03-02T12:52:49.083Z" }, + { url = "https://files.pythonhosted.org/packages/d9/d3/a845c6993a728b8b6bdce9b500d15c3ec3663cd95d2bbf9c1b8cfd519b17/confluent_kafka-2.13.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e259c0d2b9a7e16211b45404f62869502246ac3d03e35a1f80720fd09d262457", size = 3635348, upload-time = "2026-03-02T12:52:50.927Z" }, + { url = "https://files.pythonhosted.org/packages/ab/22/1cb998f7b3ee613d5b29f4b98e4a7539776eb0819b89d7c3cdd19a685692/confluent_kafka-2.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:77ea4ceccdbb67498787b7c02cc329c32417bb730e9383f46c74eb9c5851763c", size = 3194667, upload-time = "2026-03-02T12:52:53.468Z" }, + { url = "https://files.pythonhosted.org/packages/11/38/8a1b12321068e8ae126e62600a55d7a1872f969e1de5ec7f602e0dba8394/confluent_kafka-2.13.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a64a8967734f865f54b766553d63a40f17081cd3d2c6cfe6d3217aa7494d88fb", size = 3724453, upload-time = "2026-03-02T12:52:55.187Z" }, + { url = "https://files.pythonhosted.org/packages/5c/06/3effa66c59a69e17cc48c69ae2533699f4321fac1b46741f2e4b1aefb1e7/confluent_kafka-2.13.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e4cb7d112463ec15a01a3f0e0d20392cda6e46156a6439fcaaad2267696f5cde", size = 3980919, upload-time = "2026-03-02T12:52:56.852Z" }, + { url = "https://files.pythonhosted.org/packages/98/22/f76a8b85fad652b4d5c0a0259c8f7bb66393d2d9f277631c754c9ebe5092/confluent_kafka-2.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:44496777ff0104421b8f4bb269728e8a5e772c09f34ae813bc47110e0172ebe0", size = 4097817, upload-time = "2026-03-02T12:52:58.831Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/ae9a7f21ba49e55b1be18362cefd7648e4aceb588e254f9ee5edb97fcf44/confluent_kafka-2.13.2-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:02702808dd3cfd91f117fbf17181da2a95392967e9f946b1cbdc5589b36e39d1", size = 3199459, upload-time = "2026-03-02T12:53:00.614Z" }, + { url = "https://files.pythonhosted.org/packages/12/94/ccd92f9a3bb685b265bc83ede699651aa526502e4988e906e710d3f24cd3/confluent_kafka-2.13.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:7dc3a2da92638c077bbabb07058f1938078b42a89f0bbfdcb852d4289c2de27e", size = 3638743, upload-time = "2026-03-02T12:53:01.951Z" }, + { url = "https://files.pythonhosted.org/packages/ba/66/048925a546a0f8e9134a89441aa4ae663892839004668d1039d5f9dd8d45/confluent_kafka-2.13.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f3e6d010ad38447a48e0f9fab81edd4d2fd0b5f5a79ab475c30347689e35c6e6", size = 3724788, upload-time = "2026-03-02T12:53:03.775Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a6/53faa22d52d8fc6f58424d4b6c2c32855198fcb776ea8b4404ee50b58c72/confluent_kafka-2.13.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9161865d8246eb77d1c30233a315bdad96145af783981877664532fa212f56be", size = 3981324, upload-time = "2026-03-02T12:53:05.339Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/1578956d3721645b24c22b0e9ceeab794fffc197a32074a7572bfbc07ca7/confluent_kafka-2.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:931233798306b859f4870ec58e3951a2bd32d14ef29f944f56892851b0aafab0", size = 4157492, upload-time = "2026-03-02T12:53:06.977Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4c/46f09fcc1dedebb0a0884b072ddde74be8a8bcfb5e3fbc912bd2c8255e6f/confluent_kafka-2.13.2-cp314-cp314-macosx_13_0_arm64.whl", hash = "sha256:9cb0d6820107deca1823d68b96831bd982d0a11c4e6bcf0a12e8040192c48a8f", size = 3199305, upload-time = "2026-03-02T12:53:08.351Z" }, + { url = "https://files.pythonhosted.org/packages/37/3c/56d052bdedb7d4bb56bf993dc017df4434e2eb5e73745f22d0beb3c32999/confluent_kafka-2.13.2-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:b31d94bca493d84927927d1bdd59e1b6d3d921019a657f99f0c8cc5da8c85311", size = 3638586, upload-time = "2026-03-02T12:53:10.01Z" }, + { url = "https://files.pythonhosted.org/packages/33/7a/2bfc9e9341d50813674d3db6425ac4cb963764bffdf589774f94c0cbf852/confluent_kafka-2.13.2-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:f09adb42fb898a0b3a88b02e77bee472e93f758258945386c77864016b4e4efc", size = 3724554, upload-time = "2026-03-02T12:53:11.682Z" }, + { url = "https://files.pythonhosted.org/packages/cf/bb/0d0cdad1763044f3e06bea52c3332256b17f3e64c04a8214ee217fc68ab0/confluent_kafka-2.13.2-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:fa3be1fe231e06b2c7501fa3641b30ea90ea17be79ca89806eef22ff34ed106c", size = 3981002, upload-time = "2026-03-02T12:53:13.399Z" }, + { url = "https://files.pythonhosted.org/packages/69/65/361ace93de20ab5d83dc0d108389b29f4549f478e0b8aa0f19baf597c0f0/confluent_kafka-2.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:a8d1e0721de378034ecc928b47238272b56bf20af5dd504233bcb93ce07a38a6", size = 4275836, upload-time = "2026-03-02T12:53:14.703Z" }, ] [[package]] @@ -2642,6 +2656,29 @@ dependencies = [ { name = "typing-extensions" }, ] +[[package]] +name = "opentelemetry-distro" +source = { editable = "opentelemetry-distro" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-sdk" }, +] + +[package.optional-dependencies] +otlp = [ + { name = "opentelemetry-exporter-otlp" }, +] + +[package.metadata] +requires-dist = [ + { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, + { name = "opentelemetry-exporter-otlp", marker = "extra == 'otlp'", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=exporter%2Fopentelemetry-exporter-otlp&branch=main" }, + { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, + { name = "opentelemetry-sdk", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-sdk&branch=main" }, +] +provides-extras = ["otlp"] + [[package]] name = "opentelemetry-exporter-credential-provider-gcp" source = { editable = "exporter/opentelemetry-exporter-credential-provider-gcp" } @@ -2658,6 +2695,51 @@ requires-dist = [ { name = "requests", specifier = ">=2.7" }, ] +[[package]] +name = "opentelemetry-exporter-otlp" +version = "1.40.0.dev0" +source = { git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=exporter%2Fopentelemetry-exporter-otlp&branch=main#f32b684a1ccd6d5eb658ec0b45bf18928ea430e4" } +dependencies = [ + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0.dev0" +source = { git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=exporter%2Fopentelemetry-exporter-otlp-proto-common&branch=main#f32b684a1ccd6d5eb658ec0b45bf18928ea430e4" } +dependencies = [ + { name = "opentelemetry-proto" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0.dev0" +source = { git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=exporter%2Fopentelemetry-exporter-otlp-proto-grpc&branch=main#f32b684a1ccd6d5eb658ec0b45bf18928ea430e4" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.40.0.dev0" +source = { git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=exporter%2Fopentelemetry-exporter-otlp-proto-http&branch=main#f32b684a1ccd6d5eb658ec0b45bf18928ea430e4" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, + { name = "typing-extensions" }, +] + [[package]] name = "opentelemetry-exporter-prometheus-remote-write" source = { editable = "exporter/opentelemetry-exporter-prometheus-remote-write" } @@ -2711,7 +2793,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "packaging", specifier = ">=18.0" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] [[package]] @@ -2733,7 +2815,7 @@ requires-dist = [ { name = "aio-pika", marker = "extra == 'instruments'", specifier = ">=7.2.0,<10.0.0" }, { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -2760,7 +2842,7 @@ requires-dist = [ { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "opentelemetry-util-http", editable = "util/opentelemetry-util-http" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -2787,7 +2869,7 @@ requires-dist = [ { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "opentelemetry-util-http", editable = "util/opentelemetry-util-http" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -2837,7 +2919,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-instrumentation-dbapi", editable = "instrumentation/opentelemetry-instrumentation-dbapi" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -2858,7 +2940,7 @@ instruments = [ [package.metadata] requires-dist = [ - { name = "anthropic", marker = "extra == 'instruments'", specifier = ">=0.16.0" }, + { name = "anthropic", marker = "extra == 'instruments'", specifier = ">=0.51.0" }, { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, @@ -2936,7 +3018,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -2980,29 +3062,6 @@ requires-dist = [ ] provides-extras = ["instruments"] -[[package]] -name = "opentelemetry-instrumentation-boto" -source = { editable = "instrumentation/opentelemetry-instrumentation-boto" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-instrumentation" }, - { name = "opentelemetry-semantic-conventions" }, -] - -[package.optional-dependencies] -instruments = [ - { name = "boto" }, -] - -[package.metadata] -requires-dist = [ - { name = "boto", marker = "extra == 'instruments'", specifier = "~=2.0" }, - { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, - { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, - { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, -] -provides-extras = ["instruments"] - [[package]] name = "opentelemetry-instrumentation-boto3sqs" source = { editable = "instrumentation/opentelemetry-instrumentation-boto3sqs" } @@ -3024,7 +3083,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3036,22 +3095,26 @@ dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-propagator-aws-xray" }, { name = "opentelemetry-semantic-conventions" }, + { name = "wrapt" }, ] [package.optional-dependencies] -instruments = [ +instruments-any = [ + { name = "aiobotocore" }, { name = "botocore" }, ] [package.metadata] requires-dist = [ - { name = "botocore", marker = "extra == 'instruments'", specifier = "~=1.0" }, + { name = "aiobotocore", marker = "extra == 'instruments-any'", specifier = "~=2.0" }, + { name = "botocore", marker = "extra == 'instruments-any'", specifier = "~=1.0" }, { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-propagator-aws-xray", editable = "propagator/opentelemetry-propagator-aws-xray" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] -provides-extras = ["instruments"] +provides-extras = ["instruments", "instruments-any"] [[package]] name = "opentelemetry-instrumentation-cassandra" @@ -3076,7 +3139,7 @@ requires-dist = [ { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "scylla-driver", marker = "extra == 'instruments-any'", specifier = "~=3.25" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments", "instruments-any"] @@ -3149,7 +3212,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3169,10 +3232,10 @@ instruments = [ [package.metadata] requires-dist = [ - { name = "confluent-kafka", marker = "extra == 'instruments'", specifier = ">=1.8.2,<=2.13.0" }, + { name = "confluent-kafka", marker = "extra == 'instruments'", specifier = ">=1.8.2,<3.0.0" }, { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3191,7 +3254,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3248,7 +3311,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3383,7 +3446,7 @@ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3410,7 +3473,7 @@ requires-dist = [ { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "opentelemetry-util-http", editable = "util/opentelemetry-util-http" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3433,7 +3496,7 @@ requires-dist = [ { name = "jinja2", marker = "extra == 'instruments'", specifier = ">=2.7,<4.0" }, { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3613,7 +3676,7 @@ requires-dist = [ { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "packaging", specifier = ">=20.0" }, { name = "pika", marker = "extra == 'instruments'", specifier = ">=0.12.0" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3686,7 +3749,7 @@ requires-dist = [ { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "pymemcache", marker = "extra == 'instruments'", specifier = ">=1.3.5,<5" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3784,7 +3847,7 @@ requires-dist = [ { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "opentelemetry-util-http", editable = "util/opentelemetry-util-http" }, { name = "pyramid", marker = "extra == 'instruments'", specifier = ">=1.7" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -3969,7 +4032,7 @@ dependencies = [ requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -4065,7 +4128,7 @@ requires-dist = [ { name = "opentelemetry-semantic-conventions", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-semantic-conventions&branch=main" }, { name = "opentelemetry-util-http", editable = "util/opentelemetry-util-http" }, { name = "urllib3", marker = "extra == 'instruments'", specifier = ">=1.0.0,<3.0.0" }, - { name = "wrapt", specifier = ">=1.0.0,<2.0.0" }, + { name = "wrapt", specifier = ">=1.0.0,<3.0.0" }, ] provides-extras = ["instruments"] @@ -4160,12 +4223,21 @@ requires-dist = [ { name = "opentelemetry-sdk", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-sdk&branch=main" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0.dev0" +source = { git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-proto&branch=main#f32b684a1ccd6d5eb658ec0b45bf18928ea430e4" } +dependencies = [ + { name = "protobuf" }, +] + [[package]] name = "opentelemetry-python-contrib" version = "0.0.0" source = { virtual = "." } dependencies = [ { name = "opentelemetry-api" }, + { name = "opentelemetry-distro" }, { name = "opentelemetry-exporter-prometheus-remote-write" }, { name = "opentelemetry-exporter-richconsole" }, { name = "opentelemetry-instrumentation" }, @@ -4178,9 +4250,8 @@ dependencies = [ { name = "opentelemetry-instrumentation-asyncio" }, { name = "opentelemetry-instrumentation-asyncpg", extra = ["instruments"] }, { name = "opentelemetry-instrumentation-aws-lambda" }, - { name = "opentelemetry-instrumentation-boto", extra = ["instruments"] }, { name = "opentelemetry-instrumentation-boto3sqs", extra = ["instruments"] }, - { name = "opentelemetry-instrumentation-botocore", extra = ["instruments"] }, + { name = "opentelemetry-instrumentation-botocore", extra = ["instruments-any"] }, { name = "opentelemetry-instrumentation-cassandra" }, { name = "opentelemetry-instrumentation-celery", extra = ["instruments"] }, { name = "opentelemetry-instrumentation-click", extra = ["instruments"] }, @@ -4238,6 +4309,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "opentelemetry-api", git = "https://github.com/open-telemetry/opentelemetry-python?subdirectory=opentelemetry-api&branch=main" }, + { name = "opentelemetry-distro", editable = "opentelemetry-distro" }, { name = "opentelemetry-exporter-prometheus-remote-write", editable = "exporter/opentelemetry-exporter-prometheus-remote-write" }, { name = "opentelemetry-exporter-richconsole", editable = "exporter/opentelemetry-exporter-richconsole" }, { name = "opentelemetry-instrumentation", editable = "opentelemetry-instrumentation" }, @@ -4250,9 +4322,8 @@ requires-dist = [ { name = "opentelemetry-instrumentation-asyncio", editable = "instrumentation/opentelemetry-instrumentation-asyncio" }, { name = "opentelemetry-instrumentation-asyncpg", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-asyncpg" }, { name = "opentelemetry-instrumentation-aws-lambda", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-aws-lambda" }, - { name = "opentelemetry-instrumentation-boto", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-boto" }, { name = "opentelemetry-instrumentation-boto3sqs", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-boto3sqs" }, - { name = "opentelemetry-instrumentation-botocore", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-botocore" }, + { name = "opentelemetry-instrumentation-botocore", extras = ["instruments-any"], editable = "instrumentation/opentelemetry-instrumentation-botocore" }, { name = "opentelemetry-instrumentation-cassandra", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-cassandra" }, { name = "opentelemetry-instrumentation-celery", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-celery" }, { name = "opentelemetry-instrumentation-click", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-click" }, @@ -5647,14 +5718,14 @@ wheels = [ [[package]] name = "s3transfer" -version = "0.16.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple/" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bb/940d6af975948c1cc18f44545ffb219d3c35d78ec972b42ae229e8e37e08/s3transfer-0.15.0.tar.gz", hash = "sha256:d36fac8d0e3603eff9b5bfa4282c7ce6feb0301a633566153cbd0b93d11d8379", size = 152185, upload-time = "2025-11-20T20:28:56.327Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e1/5ef25f52973aa12a19cf4e1375d00932d7fb354ffd310487ba7d44225c1a/s3transfer-0.15.0-py3-none-any.whl", hash = "sha256:6f8bf5caa31a0865c4081186689db1b2534cef721d104eb26101de4b9d6a5852", size = 85984, upload-time = "2025-11-20T20:28:55.046Z" }, ] [[package]]