Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package 
python-opentelemetry-instrumentation-fastapi for openSUSE:Factory checked in at 
2026-06-27 18:04:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing 
/work/SRC/openSUSE:Factory/python-opentelemetry-instrumentation-fastapi (Old)
 and      
/work/SRC/openSUSE:Factory/.python-opentelemetry-instrumentation-fastapi.new.11887
 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-opentelemetry-instrumentation-fastapi"

Sat Jun 27 18:04:28 2026 rev:11 rq:1361293 version:0.63b1

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-opentelemetry-instrumentation-fastapi/python-opentelemetry-instrumentation-fastapi.changes
        2026-04-28 11:57:07.675392924 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-opentelemetry-instrumentation-fastapi.new.11887/python-opentelemetry-instrumentation-fastapi.changes
     2026-06-27 18:05:31.146805713 +0200
@@ -1,0 +2,195 @@
+Tue Jun 23 08:15:26 UTC 2026 - Steve Kowalik <[email protected]>
+
+- Add patch support-fastapi-0.137.patch:
+  * Support fastapi 0.137 changes.
+
+-------------------------------------------------------------------
+Sat Jun 20 16:12:39 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.63b1:
+  * This is a patch release on the previous 1.42.0/0.63b0
+    release, fixing the issue(s) below.
+
+- update to 0.63b0:
+  * ### Added
+  * `opentelemetry-exporter-richconsole`: Add support for
+    suppressing resource information
+  * `opentelemetry-instrumentation`: Add experimental metrics
+    attributes Labeler utility
+  * `opentelemetry-instrumentation-logging`: Add
+    `OTEL_PYTHON_LOG_HANDLER_LEVEL` and `OTEL_PYTHON_LOG_FORMAT`
+    environment variables to configure the log level and
+    formatter of the auto-instrumented `LoggingHandler`.
+  * `opentelemetry-instrumentation-sqlite3`: Add uninstrument,
+    error status, suppress, and no-op tests
+  * Add `BaggageLogProcessor` to `opentelemetry-processor-
+    baggage`
+  * `opentelemetry-instrumentation-system-metrics`: Add support
+    for `process.disk.io` metric in system-metrics
+    instrumentation
+  * `opentelemetry-instrumentation`: Register
+    `OTEL_SEMCONV_STABILITY_OPT_IN` in `environment_variables.py`
+    so `opentelemetry-instrument` exposes a
+    `--semconv_stability_opt_in` CLI argument
+  * Expand `AGENTS.md` with instrumentation/GenAI guidance and
+    add PR review instructions.
+  * `opentelemetry-instrumentation`: update auto-instrumentation
+    to re-inject instrumentation path after init
+  * `opentelemetry-instrumentation-dbapi`: Add Database client
+    operation duration and returned rows metrics
+  * ### Changed
+  * Remove redundant `pylint: disable=attribute-defined-outside-
+    init` comments and add rule to global `.pylintrc` disable
+    list
+  * Bump `pylint` to `4.0.5`
+  * `opentelemetry-instrumentation-logging`: Use
+    `LogRecord.getMessage()` to format and extract each log
+    record's body text to more closely match the expected usage
+    of the logging system. As a result, all OTel log record
+    bodies are now always strings. Previously, if `LogRecord.msg`
+    (which contains the format string) was set to a non-string
+    object (e.g. `logger.warning(some_dict)`), the object was
+    exported as-is to the OTLP body field. Now,
+    `LogRecord.getMessage()` will convert it to to a string. If
+    you are passing in non-strings as the format string argument
+    and your backend is expecting them as-is, you will need to
+    update accordingly.
+  * Switch to SPDX license headers and add CI enforcement
+  * `opentelemetry-instrumentation-{urllib,urllib3,requests}`:
+    switch http mock library from abandoned httpretty to mocket
+  * ### Removed
+  * Drop Python 3.9 support
+  * ### Fixed
+  * `opentelemetry-instrumentation-aiohttp-server`: Use
+    `canonical` attribute of the `Resource` as a span name
+  * Refactor unit tests to allow for population of the random
+    trace id flag in the `traceparent` header
+  * `opentelemetry-instrumentation-aws-lambda`: fix improper
+    handling of header casing
+  * `opentelemetry-instrumentation-flask`: Clean up environ keys
+    in `_teardown_request` to prevent duplicate execution
+  * `opentelemetry-instrumentation-celery`: Coerce non-string
+    values to strings in `CeleryGetter.get()` to prevent
+    `TypeError` in `TraceState.from_header()` when Celery request
+    attributes contain ints
+  * `opentelemetry-instrumentation-celery`: Coerce timelimit
+    values to strings in `set_attributes_from_context()` to
+    prevent mixed-type span attribute warning
+  * `opentelemetry-instrumentation-fastapi`: Fix `FastAPI`
+    instrumentation to correctly trace `BackgroundTasks` by
+    wrapping their execution in a dedicated span, ensuring proper
+    parent-child relationships and accurate trace timing
+  * `opentelemetry-instrumentation-flask`: Stop reading the
+    deprecated (from 3.1) `flask.__version__` attribute; resolve
+    the Flask version via `importlib.metadata`
+  * `opentelemetry-instrumentation-confluent-kafka`: Populate
+    `server.address` and `server.port` span attributes from the
+    producer/consumer `bootstrap.servers` config; previously
+    `KafkaPropertiesExtractor.extract_bootstrap_servers` was
+    defined but never called
+  * `opentelemetry-instrumentation-dbapi` Use `ObjectProxy`
+    instead of `BaseObjectProxy` for `TracedCursorProxy` to
+    restore iterability with wrapt 2.x
+  * `opentelemetry-instrumentation-pyramid`: add missing
+    `http.response.status_code` in duration metrics for stable
+    http semantic conventions
+  * `opentelemetry-instrumentation-pika` Use `ObjectProxy`
+    instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to
+    restore iterability with wrapt 2.x
+  * `docker-tests`: Don't require sudo, debian based distro and
+    MS SQL ODBC driver to run locally. Instead require docker and
+    unixodbc
+  * `opentelemetry-instrumentation-celery`: clear completed task
+    ids from `task_id_to_start_time`
+  * `opentelemetry-instrumentation-celery`: add null guards and
+    type-safe helper handling around Celery context propagation
+    internals
+  * `opentelemetry-instrumentation-wsgi`: use `PATH_INFO` and
+    `QUERY_STRING` for URL attributes instead of parsing
+    `RAW_URI` or `REQUEST_URI`
+  * `opentelemetry-instrumentation-mysqlclient`: Update unit
+    tests to properly validate trace context trace flag values.
+  * `opentelemetry-instrumentation-pika`: pass destination to
+    `_enrich_span` instead of `task_name`
+  * `opentelemetry-instrumentation-tornado`: reduce cardinality
+    of span names and metrics attributes. This introduces a
+    breaking change in the metrics attributes for the stable
+    semantic convention by dropping the out of spec `url.query`
+    and `url.path` attributes in favor of in-spec `http.route`.
+  * `opentelemetry-instrumentation-confluent-kafka`: Declare
+    `opentelemetry-semantic-conventions` as a direct dependency
+  * `opentelemetry-instrumentation-pymssql`: Fix semconv
+    stability migration for connection attributes (host, port,
+    user) set in `wrapped_connection()` to respect
+    `OTEL_SEMCONV_STABILITY_OPT_IN`. Note: `net.peer.port` is now
+    emitted as `int` instead of `string` in default mode,
+    aligning with other DB instrumentations.
+  * Declare `opentelemetry-semantic-conventions` as a direct
+    dependency for the aio-pika, logging, pika and system-metrics
+    instrumentations, since each imports `opentelemetry.semconv`
+    directly.
+- update to opentelemetry-instrumentation-google-genai==0.7b1:
+  * This is a patch release on the previous 0.7b0 release, fixing
+    the issue(s) below.
+- update to opentelemetry-util-genai==0.4b0:
+  * Add `AgentInvocation` type with `invoke_agent` span lifecycle
+  * Add metrics support for EmbeddingInvocation
+  * Add support for workflow in genAI utils handler.
+  * Enrich ToolCall type, breaking change: usage of ToolCall
+    class renamed to ToolCallRequest
+  * Add EmbeddingInvocation span lifecycle support
+  * Populate schema_url on metrics
+  * Add workflow invocation type to genAI utils
+  * Check if upload works at startup in initializer of the
+    `UploadCompletionHook`, instead of repeatedly failing on
+    every upload (#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.
+  * `TelemetryHandler` now accepts a `completion_hook` parameter
+    and calls it after each LLM invocation, passing inputs,
+    outputs, the active span, and the log record. Content capture
+    is enabled automatically when a real hook is configured.
+  * Add metrics to ToolInvocations
+  * Wrap completion hooks loaded via `load_completion_hook` so
+    exceptions raised by `on_completion` are logged and swallowed
+    instead of propagating to instrumentation call sites.
+- update to opentelemetry-instrumentation-openai-v2==2.4b0:
+  * Migrate experimental path from deprecated `LLMInvocation` to
+    `InferenceInvocation`, using `handler.start_inference()` and
+    `invocation.stop()`/`invocation.fail()` directly
+  * Use `create_duration_histogram` and `create_token_histogram`
+    from `opentelemetry-util-genai` instead of defining bucket
+    boundaries locally
+  * Import `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`
+    from `opentelemetry.util.genai.environment_variables` instead
+    of re-defining it locally, making `opentelemetry-util-genai`
+    the single source of truth for this constant.
+  * Fix compatibility with wrapt 2.x by using positional
+    arguments in `wrap_function_wrapper()` calls
+  * Fix `ChoiceBuffer` crash on streaming tool-call deltas with
+    `arguments=None`
+  * Fix `StreamWrapper` missing `.headers` and other attributes
+    when using `with_raw_response` streaming
+  * 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.
+  * Add wrappers for OpenAI Responses API streams and response
+    stream managers
+  * Add async wrappers for OpenAI Responses API streams and
+    response stream managers
+  * Add strongly typed Responses API extractors with validation
+    and content extraction improvements
+  * Add completion hook support.
+  * Fix `response_format` handling: map
+    `json_object`/`json_schema` to `json` output type.
+  * Skip attribute values with `openai.Omit` value.
+  * Default empty string for `gen_ai.request.model` attribute on
+    missing model.
+
+-------------------------------------------------------------------

Old:
----
  opentelemetry_instrumentation_fastapi-0.62b1.tar.gz

New:
----
  opentelemetry_instrumentation_fastapi-0.63b1.tar.gz
  support-fastapi-0.137.patch

----------(New B)----------
  New:
- Add patch support-fastapi-0.137.patch:
  * Support fastapi 0.137 changes.
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-opentelemetry-instrumentation-fastapi.spec ++++++
--- /var/tmp/diff_new_pack.7wpftZ/_old  2026-06-27 18:05:32.610854792 +0200
+++ /var/tmp/diff_new_pack.7wpftZ/_new  2026-06-27 18:05:32.614854925 +0200
@@ -27,12 +27,15 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-opentelemetry-instrumentation-fastapi%{?psuffix}
-Version:        0.62b1
+Version:        0.63b1
 Release:        0
 Summary:        OpenTelemetry FastAPI Instrumentation
 License:        Apache-2.0
 URL:            
https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-fastapi
 Source:         
https://files.pythonhosted.org/packages/source/o/opentelemetry_instrumentation_fastapi/opentelemetry_instrumentation_fastapi-%{version}.tar.gz
+# PATCH-FIX-UPSTREAM Based on 
gh#open-telemetry/opentelemetry-python-contrib#4700
+Patch0:         support-fastapi-0.137.patch
+BuildRequires:  %{python_module base >= 3.10}
 BuildRequires:  %{python_module hatchling}
 BuildRequires:  %{python_module pip}
 BuildRequires:  python-rpm-macros
@@ -64,7 +67,7 @@
 Auto-instrumentation using the opentelemetry-instrumentation package is also 
supported.
 
 %prep
-%setup -q -n opentelemetry_instrumentation_fastapi-%{version}
+%autosetup -p1 -n opentelemetry_instrumentation_fastapi-%{version}
 
 %if !%{with test}
 %build

++++++ opentelemetry_instrumentation_fastapi-0.62b1.tar.gz -> 
opentelemetry_instrumentation_fastapi-0.63b1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/.gitignore 
new/opentelemetry_instrumentation_fastapi-0.63b1/.gitignore
--- old/opentelemetry_instrumentation_fastapi-0.62b1/.gitignore 2020-02-02 
01:00:00.000000000 +0100
+++ new/opentelemetry_instrumentation_fastapi-0.63b1/.gitignore 2020-02-02 
01:00:00.000000000 +0100
@@ -64,3 +64,5 @@
 
 # opentelemetry-admin jobs
 opentelemetry-admin-jobs.txt
+
+.claude/settings.local.json
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/PKG-INFO 
new/opentelemetry_instrumentation_fastapi-0.63b1/PKG-INFO
--- old/opentelemetry_instrumentation_fastapi-0.62b1/PKG-INFO   2020-02-02 
01:00:00.000000000 +0100
+++ new/opentelemetry_instrumentation_fastapi-0.63b1/PKG-INFO   2020-02-02 
01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: opentelemetry-instrumentation-fastapi
-Version: 0.62b1
+Version: 0.63b1
 Summary: OpenTelemetry FastAPI Instrumentation
 Project-URL: Homepage, 
https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-fastapi
 Project-URL: Repository, 
https://github.com/open-telemetry/opentelemetry-python-contrib
@@ -12,18 +12,17 @@
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
 Classifier: Programming Language :: Python :: 3.14
-Requires-Python: >=3.9
+Requires-Python: >=3.10
 Requires-Dist: opentelemetry-api~=1.12
-Requires-Dist: opentelemetry-instrumentation-asgi==0.62b1
-Requires-Dist: opentelemetry-instrumentation==0.62b1
-Requires-Dist: opentelemetry-semantic-conventions==0.62b1
-Requires-Dist: opentelemetry-util-http==0.62b1
+Requires-Dist: opentelemetry-instrumentation-asgi==0.63b1
+Requires-Dist: opentelemetry-instrumentation==0.63b1
+Requires-Dist: opentelemetry-semantic-conventions==0.63b1
+Requires-Dist: opentelemetry-util-http==0.63b1
 Provides-Extra: instruments
 Requires-Dist: fastapi~=0.92; extra == 'instruments'
 Description-Content-Type: text/x-rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/pyproject.toml 
new/opentelemetry_instrumentation_fastapi-0.63b1/pyproject.toml
--- old/opentelemetry_instrumentation_fastapi-0.62b1/pyproject.toml     
2020-02-02 01:00:00.000000000 +0100
+++ new/opentelemetry_instrumentation_fastapi-0.63b1/pyproject.toml     
2020-02-02 01:00:00.000000000 +0100
@@ -8,7 +8,7 @@
 description = "OpenTelemetry FastAPI Instrumentation"
 readme = "README.rst"
 license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
 authors = [
   { name = "OpenTelemetry Authors", email = 
"[email protected]" },
 ]
@@ -18,7 +18,6 @@
   "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 @@
 ]
 dependencies = [
   "opentelemetry-api ~= 1.12",
-  "opentelemetry-instrumentation == 0.62b1",
-  "opentelemetry-instrumentation-asgi == 0.62b1",
-  "opentelemetry-semantic-conventions == 0.62b1",
-  "opentelemetry-util-http == 0.62b1",
+  "opentelemetry-instrumentation == 0.63b1",
+  "opentelemetry-instrumentation-asgi == 0.63b1",
+  "opentelemetry-semantic-conventions == 0.63b1",
+  "opentelemetry-util-http == 0.63b1",
 ]
 
 [project.optional-dependencies]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/src/opentelemetry/instrumentation/fastapi/__init__.py
 
new/opentelemetry_instrumentation_fastapi-0.63b1/src/opentelemetry/instrumentation/fastapi/__init__.py
--- 
old/opentelemetry_instrumentation_fastapi-0.62b1/src/opentelemetry/instrumentation/fastapi/__init__.py
      2020-02-02 01:00:00.000000000 +0100
+++ 
new/opentelemetry_instrumentation_fastapi-0.63b1/src/opentelemetry/instrumentation/fastapi/__init__.py
      2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
 # 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.
+# SPDX-License-Identifier: Apache-2.0
 
 """
 Usage
@@ -190,6 +179,7 @@
 
 import fastapi
 from starlette.applications import Starlette
+from starlette.background import BackgroundTask
 from starlette.middleware.errors import ServerErrorMiddleware
 from starlette.routing import Match, Route
 from starlette.types import ASGIApp, Receive, Scope, Send
@@ -399,6 +389,16 @@
                 app,
             )
 
+            if not hasattr(BackgroundTask, "_otel_original_call"):
+                BackgroundTask._otel_original_call = BackgroundTask.__call__
+
+                async def traced_call(self):
+                    span_name = f"BackgroundTask {getattr(self.func, 
'__name__', self.func.__class__.__name__)}"
+                    with tracer.start_as_current_span(span_name):
+                        return await BackgroundTask._otel_original_call(self)
+
+                BackgroundTask.__call__ = traced_call
+
             app._is_instrumented_by_opentelemetry = True
             if app not in _InstrumentedFastAPI._instrumented_fastapi_apps:
                 _InstrumentedFastAPI._instrumented_fastapi_apps.add(app)
@@ -416,6 +416,11 @@
             app.build_middleware_stack = original_build_middleware_stack
             del app._original_build_middleware_stack
         app.middleware_stack = app.build_middleware_stack()
+
+        if hasattr(BackgroundTask, "_otel_original_call"):
+            BackgroundTask.__call__ = BackgroundTask._otel_original_call
+            del BackgroundTask._otel_original_call
+
         app._is_instrumented_by_opentelemetry = False
 
         # Remove the app from the set of instrumented apps to avoid calling 
uninstrument twice
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/src/opentelemetry/instrumentation/fastapi/package.py
 
new/opentelemetry_instrumentation_fastapi-0.63b1/src/opentelemetry/instrumentation/fastapi/package.py
--- 
old/opentelemetry_instrumentation_fastapi-0.62b1/src/opentelemetry/instrumentation/fastapi/package.py
       2020-02-02 01:00:00.000000000 +0100
+++ 
new/opentelemetry_instrumentation_fastapi-0.63b1/src/opentelemetry/instrumentation/fastapi/package.py
       2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
 # 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.
+# SPDX-License-Identifier: Apache-2.0
 
 
 _instruments = ("fastapi ~= 0.92",)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/src/opentelemetry/instrumentation/fastapi/version.py
 
new/opentelemetry_instrumentation_fastapi-0.63b1/src/opentelemetry/instrumentation/fastapi/version.py
--- 
old/opentelemetry_instrumentation_fastapi-0.62b1/src/opentelemetry/instrumentation/fastapi/version.py
       2020-02-02 01:00:00.000000000 +0100
+++ 
new/opentelemetry_instrumentation_fastapi-0.63b1/src/opentelemetry/instrumentation/fastapi/version.py
       2020-02-02 01:00:00.000000000 +0100
@@ -1,15 +1,4 @@
 # 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.
+# SPDX-License-Identifier: Apache-2.0
 
-__version__ = "0.62b1"
+__version__ = "0.63b1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/tests/test_fastapi_instrumentation.py
 
new/opentelemetry_instrumentation_fastapi-0.63b1/tests/test_fastapi_instrumentation.py
--- 
old/opentelemetry_instrumentation_fastapi-0.62b1/tests/test_fastapi_instrumentation.py
      2020-02-02 01:00:00.000000000 +0100
+++ 
new/opentelemetry_instrumentation_fastapi-0.63b1/tests/test_fastapi_instrumentation.py
      2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
 # 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.
+# SPDX-License-Identifier: Apache-2.0
 
 # pylint: disable=too-many-lines
 
@@ -25,11 +14,13 @@
 
 import fastapi
 import pytest
+from fastapi.background import BackgroundTasks
 from fastapi.middleware.asyncexitstack import AsyncExitStackMiddleware
 from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
 from fastapi.responses import JSONResponse, PlainTextResponse
 from fastapi.routing import APIRoute
 from fastapi.testclient import TestClient
+from starlette.background import BackgroundTask
 from starlette.routing import Match
 from starlette.types import Receive, Scope, Send
 
@@ -493,6 +484,51 @@
         for span in spans:
             self.assertIn("GET /foobar", span.name)
 
+    def test_background_task_span_parents_inner_spans(self):
+        """Regression test for #4251: spans created inside a FastAPI
+        BackgroundTask must be children of a dedicated background-task span
+        instead of the already-closed request span."""
+        self.memory_exporter.clear()
+        app = fastapi.FastAPI()
+        self._instrumentor.instrument_app(app)
+        tracer = self.tracer_provider.get_tracer(__name__)
+
+        async def background_notify():
+            with tracer.start_as_current_span("inside-background-task"):
+                pass
+
+        @app.post("/checkout")
+        async def checkout(background_tasks: BackgroundTasks):
+            background_tasks.add_task(background_notify)
+            return {"status": "processing"}
+
+        with TestClient(app) as client:
+            response = client.post("/checkout")
+        self.assertEqual(200, response.status_code)
+        spans = self.memory_exporter.get_finished_spans()
+        request_span = next(
+            span for span in spans if span.name == "POST /checkout"
+        )
+        background_span = next(
+            span
+            for span in spans
+            if span.name == "BackgroundTask background_notify"
+        )
+        inner_span = next(
+            span for span in spans if span.name == "inside-background-task"
+        )
+        self.assertIsNotNone(background_span.parent)
+        self.assertEqual(
+            background_span.parent.span_id,
+            request_span.context.span_id,
+        )
+        self.assertIsNotNone(inner_span.parent)
+        self.assertEqual(
+            inner_span.parent.span_id,
+            background_span.context.span_id,
+        )
+        otel_fastapi.FastAPIInstrumentor().uninstrument_app(app)
+
     def test_fastapi_route_attribute_added(self):
         """Ensure that fastapi routes are used as the span name."""
         self._client.get("/user/123")
@@ -988,6 +1024,49 @@
                 if isinstance(point, NumberDataPoint):
                     self.assertEqual(point.value, 0)
 
+    def test_uninstrument_app_restores_background_task_call(self):
+        """Regression test for #4251: uninstrumentation must restore the
+        original BackgroundTask.__call__ after FastAPI patches it."""
+        self.assertTrue(hasattr(BackgroundTask, "_otel_original_call"))
+        self._instrumentor.uninstrument_app(self._app)
+        self.assertFalse(hasattr(BackgroundTask, "_otel_original_call"))
+
+    def test_background_task_span_not_duplicated_on_double_instrument_app(
+        self,
+    ):
+        """Regression test for #4251: repeated instrument_app calls must not
+        wrap BackgroundTask.__call__ multiple times or duplicate spans."""
+        self.memory_exporter.clear()
+        app = fastapi.FastAPI()
+        self._instrumentor.instrument_app(app)
+        self._instrumentor.instrument_app(app)
+        tracer = self.tracer_provider.get_tracer(__name__)
+
+        async def background_notify():
+            with tracer.start_as_current_span("inside-background-task"):
+                pass
+
+        @app.post("/checkout")
+        async def checkout(background_tasks: BackgroundTasks):
+            background_tasks.add_task(background_notify)
+            return {"status": "processing"}
+
+        with TestClient(app) as client:
+            response = client.post("/checkout")
+        self.assertEqual(200, response.status_code)
+        spans = self.memory_exporter.get_finished_spans()
+        background_spans = [
+            span
+            for span in spans
+            if span.name == "BackgroundTask background_notify"
+        ]
+        inner_spans = [
+            span for span in spans if span.name == "inside-background-task"
+        ]
+        self.assertEqual(len(background_spans), 1)
+        self.assertEqual(len(inner_spans), 1)
+        otel_fastapi.FastAPIInstrumentor().uninstrument_app(app)
+
     def test_metric_uninstrument_app(self):
         self._client.get("/foobar")
         self._instrumentor.uninstrument_app(self._app)
@@ -2063,7 +2142,6 @@
         )
 
 
-# pylint: disable=attribute-defined-outside-init
 class TestFastAPIFallback(TestBaseFastAPI):
     @pytest.fixture(autouse=True)
     def inject_fixtures(self, caplog):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/tests/test_fastapi_instrumentation_custom_headers.py
 
new/opentelemetry_instrumentation_fastapi-0.63b1/tests/test_fastapi_instrumentation_custom_headers.py
--- 
old/opentelemetry_instrumentation_fastapi-0.62b1/tests/test_fastapi_instrumentation_custom_headers.py
       2020-02-02 01:00:00.000000000 +0100
+++ 
new/opentelemetry_instrumentation_fastapi-0.63b1/tests/test_fastapi_instrumentation_custom_headers.py
       2020-02-02 01:00:00.000000000 +0100
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
 from collections.abc import Mapping
 from typing import Tuple
 from unittest.mock import patch
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/opentelemetry_instrumentation_fastapi-0.62b1/tests/test_fastapi_instrumentation_wrapped.py
 
new/opentelemetry_instrumentation_fastapi-0.63b1/tests/test_fastapi_instrumentation_wrapped.py
--- 
old/opentelemetry_instrumentation_fastapi-0.62b1/tests/test_fastapi_instrumentation_wrapped.py
      2020-02-02 01:00:00.000000000 +0100
+++ 
new/opentelemetry_instrumentation_fastapi-0.63b1/tests/test_fastapi_instrumentation_wrapped.py
      2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
 # 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.
+# SPDX-License-Identifier: Apache-2.0
 import fastapi
 from starlette.testclient import TestClient
 

++++++ support-fastapi-0.137.patch ++++++
>From c26b73b7ab91a79c6bf85c9d09ac2f6ea5ef2184 Mon Sep 17 00:00:00 2001
From: Kshitiz Jain <[email protected]>
Date: Wed, 17 Jun 2026 21:21:17 +0530
Subject: [PATCH 1/9] fastapi: handle FastAPI 0.137 _IncludedRouter routes

FastAPI 0.137 changed ``app.routes`` from a flat list into a tree that
nests ``include_router()`` routes under ``_IncludedRouter`` nodes. These
nodes have no ``path`` attribute, so ``_get_route_details()`` raised
``AttributeError`` when resolving the route for any included route.

Detect these nodes via their ``effective_route_contexts()`` and match
the flattened contexts directly. This avoids the crash, reports the full
templated route, and (by not calling ``_IncludedRouter.matches()``)
avoids re-deriving the request scope.

Fixes #4699

Assisted-by: Claude Opus 4.8
---
 .changelog/4699.fixed                              |  1 +
 .../instrumentation/fastapi/__init__.py            | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 .changelog/4699.fixed

diff --git a/.changelog/4699.fixed b/.changelog/4699.fixed
new file mode 100644
index 0000000000..7577376ff4
--- /dev/null
+++ b/.changelog/4699.fixed
@@ -0,0 +1 @@
+`opentelemetry-instrumentation-fastapi`: fix `AttributeError` when resolving 
routes added via `include_router` on FastAPI 0.137+
diff --git 
a/instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py 
b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
index ab936f0ed5..f55efd3e63 100644
--- a/src/opentelemetry/instrumentation/fastapi/__init__.py
+++ b/src/opentelemetry/instrumentation/fastapi/__init__.py
@@ -479,6 +479,20 @@ def _get_route_details(scope):
     route = None
 
     for starlette_route in app.routes:
+        # FastAPI >= 0.137 nests routes added via include_router() under
+        # _IncludedRouter tree nodes, which expose no ``path`` attribute.
+        # Flatten them into their effective route contexts, each of which
+        # provides matches() and the full templated path. Matching these
+        # directly avoids _IncludedRouter.matches() re-deriving the scope.
+        if hasattr(starlette_route, "effective_route_contexts"):
+            for route_context in starlette_route.effective_route_contexts():
+                match, _ = route_context.matches(scope)
+                if match == Match.FULL:
+                    return route_context.path
+                if match == Match.PARTIAL:
+                    route = route_context.path
+            continue
+
         match, _ = (
             Route.matches(starlette_route, scope)
             if isinstance(starlette_route, Route)

>From 9540f541a383ddc1b82700270ae7fc866d6a1eef Mon Sep 17 00:00:00 2001
From: Kshitiz Jain <[email protected]>
Date: Wed, 17 Jun 2026 21:21:42 +0530
Subject: [PATCH 2/9] fastapi: test route details for include_router routes

Add a regression test asserting that a request to an
``include_router()``-added route reports the templated ``http.route``
and does not raise ``AttributeError`` on FastAPI 0.137+.

Assisted-by: Claude Opus 4.8
---
 .../tests/test_fastapi_instrumentation.py     | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git 
a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py
 
b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py
index 991aec902f..077dae207b 100644
--- a/tests/test_fastapi_instrumentation.py
+++ b/tests/test_fastapi_instrumentation.py
@@ -358,6 +358,39 @@ def test_custom_api_router(self):
                 span.attributes[HTTP_URL],
             )
 
+    def test_included_router_route_details(self):
+        """
+        Regression test for
+        
https://github.com/open-telemetry/opentelemetry-python-contrib/issues/4699
+
+        FastAPI 0.137 added intermediate ``_IncludedRouter`` objects to
+        ``app.routes`` that do not expose a ``path`` attribute. Resolving the
+        route details for a request to an ``include_router``-added route must
+        not raise ``AttributeError`` and must still report the templated route.
+        """
+        app = fastapi.FastAPI()
+        router = fastapi.APIRouter()
+
+        @router.get("/items/{item_id}")
+        async def _read_item(item_id: str):
+            return {"item_id": item_id}
+
+        app.include_router(router, prefix="/api")
+        self._instrumentor.instrument_app(app)
+        try:
+            client = TestClient(app)
+            resp = client.get("/api/items/123")
+            self.assertEqual(200, resp.status_code)
+            spans = self.memory_exporter.get_finished_spans()
+            self.assertTrue(spans)
+            server_span = spans[-1]
+            self.assertEqual(
+                "/api/items/{item_id}", server_span.attributes[HTTP_ROUTE]
+            )
+            self.assertIn("GET /api/items/{item_id}", server_span.name)
+        finally:
+            self._instrumentor.uninstrument_app(app)
+
     def test_host_fastapi_call(self):
         client = TestClient(self._app, base_url="https://testserver2:443";)
         client.get("/")

>From 764660017cbbe1f64f743f0af0ae7f068edddd2b Mon Sep 17 00:00:00 2001
From: Kshitiz Jain <[email protected]>
Date: Thu, 18 Jun 2026 00:08:57 +0530
Subject: [PATCH 4/9] fastapi: flatten included routes to dedupe route matching

Addresses review feedback: the _IncludedRouter branch repeated the same
Match.FULL/Match.PARTIAL handling as the regular-route path. Extract the
expansion into a _flatten_routes() generator that yields effective route
contexts for _IncludedRouter nodes and the route itself otherwise, so
_get_route_details() runs a single match-and-extract loop over a uniform
list of candidates.

No behavior change; 105 tests pass on FastAPI 0.137.1.

Assisted-by: Claude Opus 4.8
---
 .../instrumentation/fastapi/__init__.py       | 33 ++++++++++---------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git 
a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
 
b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
index f55efd3e63..20dafa821f 100644
--- a/src/opentelemetry/instrumentation/fastapi/__init__.py
+++ b/src/opentelemetry/instrumentation/fastapi/__init__.py
@@ -462,6 +462,23 @@ def __init__(self, *args: Any, **kwargs: Any):
         _InstrumentedFastAPI._instrumented_fastapi_apps.add(self)
 
 
+def _flatten_routes(routes):
+    """
+    Yield the matchable routes from an app's route list.
+
+    FastAPI >= 0.137 nests routes added via include_router() under
+    _IncludedRouter tree nodes, which expose no ``path`` attribute. Expand
+    them into their effective route contexts, each of which provides
+    matches() and the full templated path. Matching these directly avoids
+    _IncludedRouter.matches() re-deriving the scope.
+    """
+    for starlette_route in routes:
+        if hasattr(starlette_route, "effective_route_contexts"):
+            yield from starlette_route.effective_route_contexts()
+        else:
+            yield starlette_route
+
+
 def _get_route_details(scope):
     """
     Function to retrieve Starlette route from scope.
@@ -478,21 +495,7 @@ def _get_route_details(scope):
     app = scope["app"]
     route = None
 
-    for starlette_route in app.routes:
-        # FastAPI >= 0.137 nests routes added via include_router() under
-        # _IncludedRouter tree nodes, which expose no ``path`` attribute.
-        # Flatten them into their effective route contexts, each of which
-        # provides matches() and the full templated path. Matching these
-        # directly avoids _IncludedRouter.matches() re-deriving the scope.
-        if hasattr(starlette_route, "effective_route_contexts"):
-            for route_context in starlette_route.effective_route_contexts():
-                match, _ = route_context.matches(scope)
-                if match == Match.FULL:
-                    return route_context.path
-                if match == Match.PARTIAL:
-                    route = route_context.path
-            continue
-
+    for starlette_route in _flatten_routes(app.routes):
         match, _ = (
             Route.matches(starlette_route, scope)
             if isinstance(starlette_route, Route)

>From de6c373ac9336fe1eb51ddab8dc95ee4c2f6f50e Mon Sep 17 00:00:00 2001
From: Kshitiz Jain <[email protected]>
Date: Thu, 18 Jun 2026 14:50:42 +0530
Subject: [PATCH 5/9] fastapi: use public iter_route_contexts() to flatten
 routes

FastAPI 0.137.2 added the public iter_route_contexts() helper for exactly
the "walk the routes to find a match" use case, flattening include_router()
trees (the 0.137 _IncludedRouter nodes) into RouteContext objects that
expose matches() and the templated path. Prefer it over reaching into the
private _IncludedRouter.effective_route_contexts() / _EffectiveRouteContext
internals.

_flatten_routes() now uses iter_route_contexts() when available and falls
back to the previous behavior otherwise, so FastAPI 0.137.0/0.137.1 (which
have _IncludedRouter but not the public helper) and versions before 0.137
(plain routes) keep working. The single match/extract loop is unchanged.

Bump the tested FastAPI to 0.137.2 (starlette stays at 1.3.1). Verified
105 passed on 0.137.2 (public path), 0.137.1 (effective_route_contexts
fallback), and 0.126.0 (plain-routes fallback).

Assisted-by: Claude Opus 4.8
---
 .../instrumentation/fastapi/__init__.py       | 25 +++++++++++++++----
 .../test-requirements.txt                     |  2 +-
 2 files changed, 21 insertions(+), 6 deletions(-)

diff --git 
a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
 
b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
index 20dafa821f..4fec55f769 100644
--- a/src/opentelemetry/instrumentation/fastapi/__init__.py
+++ b/src/opentelemetry/instrumentation/fastapi/__init__.py
@@ -184,6 +184,14 @@ def client_response_hook(span: Span, scope: dict[str, 
Any], message: dict[str, A
 from starlette.routing import Match, Route
 from starlette.types import ASGIApp, Receive, Scope, Send
 
+try:
+    # FastAPI >= 0.137.2 exposes a public helper that flattens routes added via
+    # include_router() (nested under _IncludedRouter in 0.137) into matchable
+    # RouteContext objects. Older versions don't have it; see _flatten_routes.
+    from fastapi.routing import iter_route_contexts
+except ImportError:
+    iter_route_contexts = None
+
 from opentelemetry.instrumentation._semconv import (
     _get_schema_url,
     _OpenTelemetrySemanticConventionStability,
@@ -466,12 +474,19 @@ def _flatten_routes(routes):
     """
     Yield the matchable routes from an app's route list.
 
-    FastAPI >= 0.137 nests routes added via include_router() under
-    _IncludedRouter tree nodes, which expose no ``path`` attribute. Expand
-    them into their effective route contexts, each of which provides
-    matches() and the full templated path. Matching these directly avoids
-    _IncludedRouter.matches() re-deriving the scope.
+    FastAPI 0.137 nests routes added via include_router() under _IncludedRouter
+    tree nodes, which expose no ``path`` attribute. They have to be flattened
+    into their effective route contexts (each providing matches() and the full
+    templated path) before they can be matched against a scope.
+
+    FastAPI >= 0.137.2 provides the public iter_route_contexts() for this, 
which
+    also wraps plain routes uniformly. On older versions fall back to the
+    private _IncludedRouter.effective_route_contexts(), and on FastAPI < 0.137
+    (no _IncludedRouter) the routes are already matchable as-is.
     """
+    if iter_route_contexts is not None:
+        yield from iter_route_contexts(routes)
+        return
     for starlette_route in routes:
         if hasattr(starlette_route, "effective_route_contexts"):
             yield from starlette_route.effective_route_contexts()

Reply via email to