From 48163f16afaa07b29f4f186ed752978e88813a0a Mon Sep 17 00:00:00 2001 From: Michal Martyniak Date: Tue, 27 Jan 2026 13:21:33 +0100 Subject: [PATCH 1/4] fix: support 'none' value for OTEL exporters to disable telemetry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Filter "none" from OTEL_TRACES_EXPORTER and OTEL_METRICS_EXPORTER - Return None from get_trace_provider/get_metric_provider when no exporters - Prevents NotImplementedError on plugin startup with OTEL disabled - Bump version 0.0.41 → 0.0.42 Fixes plugin crash when OTEL_TRACES_EXPORTER=none or OTEL_METRICS_EXPORTER=none --- CHANGELOG.md | 4 ++++ unstructured_platform_plugins/__version__.py | 2 +- unstructured_platform_plugins/etl_uvicorn/otel.py | 12 ++++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea55e13..da6e9ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.42 + +* **Support "none" value for OTEL_TRACES_EXPORTER and OTEL_METRICS_EXPORTER** - Filter "none" from exporter lists and return None when no exporters configured to properly disable OpenTelemetry instrumentation + ## 0.0.39 * **Remove wrap_error logic as exceptions are categorized in unstructured-ingest** diff --git a/unstructured_platform_plugins/__version__.py b/unstructured_platform_plugins/__version__.py index 592359c..89da183 100644 --- a/unstructured_platform_plugins/__version__.py +++ b/unstructured_platform_plugins/__version__.py @@ -1 +1 @@ -__version__ = "0.0.41" # pragma: no cover +__version__ = "0.0.42" # pragma: no cover diff --git a/unstructured_platform_plugins/etl_uvicorn/otel.py b/unstructured_platform_plugins/etl_uvicorn/otel.py index 5eae4e4..4654d87 100644 --- a/unstructured_platform_plugins/etl_uvicorn/otel.py +++ b/unstructured_platform_plugins/etl_uvicorn/otel.py @@ -30,9 +30,11 @@ def get_settings() -> OtelSettings: service_name = os.environ.get(OTEL_SERVICE_NAME, "unknown_service") trace_exporters = os.environ.get(OTEL_TRACES_EXPORTER) trace_exporters = trace_exporters.split(",") if trace_exporters else [] + trace_exporters = [e for e in trace_exporters if e != "none"] metric_exporters = os.environ.get(OTEL_METRICS_EXPORTER) metric_exporters = metric_exporters.split(",") if metric_exporters else [] + metric_exporters = [e for e in metric_exporters if e != "none"] return OtelSettings( service_name=service_name, trace_exporters=trace_exporters, @@ -40,8 +42,11 @@ def get_settings() -> OtelSettings: ) -def get_trace_provider() -> TracerProvider: +def get_trace_provider() -> TracerProvider | None: settings = get_settings() + if not settings["trace_exporters"]: + return None + provider = TracerProvider(resource=Resource({SERVICE_NAME: settings["service_name"]})) for trace_exporter_type in settings["trace_exporters"]: @@ -50,8 +55,11 @@ def get_trace_provider() -> TracerProvider: return provider -def get_metric_provider() -> MeterProvider: +def get_metric_provider() -> MeterProvider | None: settings = get_settings() + if not settings["metric_exporters"]: + return None + readers = [] for metric_exporter_type in settings["metric_exporters"]: readers.append(_get_metrics_reader(exporter_type=metric_exporter_type)) From ba3bff64c511a42d81ec23d4d47db8d18fc1a284 Mon Sep 17 00:00:00 2001 From: Michal Martyniak Date: Mon, 2 Mar 2026 11:51:49 +0100 Subject: [PATCH 2/4] test(otel): add regression tests for "none" exporter handling Covers get_settings() filtering, provider None returns, and the wrap_in_fastapi no-crash regression for OTEL_TRACES_EXPORTER=none and OTEL_METRICS_EXPORTER=none. --- test/test_otel.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 test/test_otel.py diff --git a/test/test_otel.py b/test/test_otel.py new file mode 100644 index 0000000..ab85c61 --- /dev/null +++ b/test/test_otel.py @@ -0,0 +1,70 @@ +import pytest +from opentelemetry.environment_variables import OTEL_METRICS_EXPORTER, OTEL_TRACES_EXPORTER +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.trace import TracerProvider + +from unstructured_platform_plugins.etl_uvicorn.otel import ( + get_metric_provider, + get_settings, + get_trace_provider, +) + + +def test_get_settings_filters_none_from_traces(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none") + settings = get_settings() + assert settings["trace_exporters"] == [] + + +def test_get_settings_filters_none_from_metrics(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none") + settings = get_settings() + assert settings["metric_exporters"] == [] + + +def test_get_settings_filters_none_from_combined_trace_exporters(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none,console") + settings = get_settings() + assert settings["trace_exporters"] == ["console"] + + +def test_get_settings_filters_none_from_combined_metric_exporters(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none,console") + settings = get_settings() + assert settings["metric_exporters"] == ["console"] + + +def test_get_trace_provider_returns_none_when_exporter_is_none(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none") + assert get_trace_provider() is None + + +def test_get_metric_provider_returns_none_when_exporter_is_none(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none") + assert get_metric_provider() is None + + +def test_get_trace_provider_returns_provider_when_exporter_set(monkeypatch): + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "console") + provider = get_trace_provider() + assert isinstance(provider, TracerProvider) + + +def test_get_metric_provider_returns_provider_when_exporter_set(monkeypatch): + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "console") + provider = get_metric_provider() + assert isinstance(provider, MeterProvider) + + +def test_wrap_in_fastapi_does_not_crash_with_none_otel_exporters(monkeypatch): + """Regression: previously crashed with NotImplementedError when none was set.""" + monkeypatch.setenv(OTEL_TRACES_EXPORTER, "none") + monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none") + + from test.assets.async_typed_dict_response import async_sample_function + + from unstructured_platform_plugins.etl_uvicorn.api_generator import wrap_in_fastapi + + # Should not raise NotImplementedError + app = wrap_in_fastapi(func=async_sample_function, plugin_id="test_plugin") + assert app is not None From 8674e98240f73b9393e57e5703a48bcbbc01b89a Mon Sep 17 00:00:00 2001 From: Michal Martyniak Date: Mon, 2 Mar 2026 11:59:51 +0100 Subject: [PATCH 3/4] fix(otel): fix ruff lint errors in test_otel.py --- test/test_otel.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_otel.py b/test/test_otel.py index ab85c61..a377f09 100644 --- a/test/test_otel.py +++ b/test/test_otel.py @@ -1,4 +1,3 @@ -import pytest from opentelemetry.environment_variables import OTEL_METRICS_EXPORTER, OTEL_TRACES_EXPORTER from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.trace import TracerProvider @@ -62,7 +61,6 @@ def test_wrap_in_fastapi_does_not_crash_with_none_otel_exporters(monkeypatch): monkeypatch.setenv(OTEL_METRICS_EXPORTER, "none") from test.assets.async_typed_dict_response import async_sample_function - from unstructured_platform_plugins.etl_uvicorn.api_generator import wrap_in_fastapi # Should not raise NotImplementedError From ec7dc85239de1209ea28b328a2db1aafff577c2e Mon Sep 17 00:00:00 2001 From: Michal Martyniak Date: Tue, 3 Mar 2026 10:26:43 +0100 Subject: [PATCH 4/4] fix: use correct Python version in release workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `matrix.python-version` (undefined in this job) with `env.PYTHON_VERSION` so the release uses Python 3.10 as intended. Also drop the unnecessary `make install-dependencies` step — the release job only needs `uv build` and the publish action. --- .github/workflows/release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 00abcb8..2a6ad8e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,14 +17,11 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ env.PYTHON_VERSION }} - name: Set up Python run: uv python install - - name: Install dependencies - run: make install-dependencies - - name: Build artifact run: | uv build