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()