diff --git a/lite_bootstrap/import_checker.py b/lite_bootstrap/import_checker.py index 2f530af..225c52c 100644 --- a/lite_bootstrap/import_checker.py +++ b/lite_bootstrap/import_checker.py @@ -14,7 +14,7 @@ def _safe_find_spec(module_name: str) -> bool: return False -is_opentelemetry_installed = find_spec("opentelemetry") is not None +is_opentelemetry_installed = _safe_find_spec("opentelemetry.trace") # opentelemetry-api provides the `opentelemetry` namespace (trace, metrics) without the # sdk. The OTel instrument imports opentelemetry.sdk.* and needs this stricter check; # api-only consumers (logging trace-injection, framework get_tracer_provider) use the diff --git a/tests/test_core_import_surface.py b/tests/test_core_import_surface.py index f16b828..14ccb56 100644 --- a/tests/test_core_import_surface.py +++ b/tests/test_core_import_surface.py @@ -1,7 +1,11 @@ import ast import pathlib +import shutil +import subprocess import sys +import typing_extensions + import lite_bootstrap @@ -46,3 +50,48 @@ def test_importing_lite_bootstrap_needs_only_the_stdlib_and_typing_extensions() } assert offenders == {} + + +def test_import_with_bare_opentelemetry_namespace(tmp_path: pathlib.Path) -> None: + (tmp_path / "opentelemetry").mkdir() + shutil.copyfile(typing_extensions.__file__, tmp_path / "typing_extensions.py") + + result = subprocess.run( # noqa: S603 + [ + sys.executable, + "-I", + "-S", + "-c", + """ +import sys +from importlib.util import find_spec + +sys.path.extend(sys.argv[1:]) +assert find_spec("opentelemetry") is not None +assert find_spec("opentelemetry.trace") is None + +import lite_bootstrap +from lite_bootstrap.bootstrappers.fastapi_bootstrapper import FastAPIOpenTelemetryInstrument +from lite_bootstrap.bootstrappers.fastmcp_bootstrapper import FastMcpOpenTelemetryInstrument +from lite_bootstrap.bootstrappers.faststream_bootstrapper import FastStreamOpenTelemetryInstrument +from lite_bootstrap.bootstrappers.litestar_bootstrapper import LitestarOpenTelemetryInstrument +from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument + +for instrument in ( + OpenTelemetryInstrument, + FastAPIOpenTelemetryInstrument, + FastMcpOpenTelemetryInstrument, + FastStreamOpenTelemetryInstrument, + LitestarOpenTelemetryInstrument, +): + assert not instrument.dependencies_installed(), instrument.__name__ +""", + str(tmp_path), + str(PACKAGE_ROOT.parent), + ], + capture_output=True, + text=True, + check=False, + ) + + assert result.returncode == 0, result.stderr diff --git a/tests/test_fastapi_bootstrap.py b/tests/test_fastapi_bootstrap.py index 99e674e..ef9612a 100644 --- a/tests/test_fastapi_bootstrap.py +++ b/tests/test_fastapi_bootstrap.py @@ -218,7 +218,7 @@ def test_fastapi_bootstrapper_apps_and_kwargs_warning(fastapi_config: FastAPICon @pytest.mark.parametrize( "package_name", [ - "opentelemetry", + "opentelemetry.trace", "sentry_sdk", "structlog", "prometheus_fastapi_instrumentator", @@ -227,7 +227,7 @@ def test_fastapi_bootstrapper_apps_and_kwargs_warning(fastapi_config: FastAPICon def test_fastapi_bootstrapper_with_missing_instrument_dependency( fastapi_config: FastAPIConfig, package_name: str ) -> None: - with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name): + with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name.removesuffix(".trace")): FastAPIBootstrapper(bootstrap_config=fastapi_config) diff --git a/tests/test_faststream_bootstrap.py b/tests/test_faststream_bootstrap.py index 1848259..a63523d 100644 --- a/tests/test_faststream_bootstrap.py +++ b/tests/test_faststream_bootstrap.py @@ -284,7 +284,7 @@ def test_faststream_bootstrapper_not_ready() -> None: @pytest.mark.parametrize( "package_name", [ - "opentelemetry", + "opentelemetry.trace", "sentry_sdk", "structlog", "prometheus_client", @@ -292,7 +292,7 @@ def test_faststream_bootstrapper_not_ready() -> None: ) def test_faststream_bootstrapper_with_missing_instrument_dependency(broker: RedisBroker, package_name: str) -> None: bootstrap_config = build_faststream_config(broker=broker) - with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name): + with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name.removesuffix(".trace")): FastStreamBootstrapper(bootstrap_config=bootstrap_config) @@ -364,7 +364,7 @@ def test_faststream_bootstrap_without_opentelemetry(broker: RedisBroker) -> None build_faststream_config(broker=broker), opentelemetry_generate_health_check_spans=True ) with emulate_package_missing_with_module_reload( - "opentelemetry", + "opentelemetry.trace", ["lite_bootstrap.bootstrappers.faststream_bootstrapper"], ): with pytest.warns(UserWarning, match="opentelemetry"): diff --git a/tests/test_free_bootstrap.py b/tests/test_free_bootstrap.py index 7f11f13..73f5ac4 100644 --- a/tests/test_free_bootstrap.py +++ b/tests/test_free_bootstrap.py @@ -135,7 +135,7 @@ def test_teardown_error_aggregates_all_failures(free_bootstrapper_config: FreeCo @pytest.mark.parametrize( "package_name", [ - "opentelemetry", + "opentelemetry.trace", "sentry_sdk", "structlog", ], @@ -143,7 +143,7 @@ def test_teardown_error_aggregates_all_failures(free_bootstrapper_config: FreeCo def test_free_bootstrapper_with_missing_instrument_dependency( free_bootstrapper_config: FreeConfig, package_name: str ) -> None: - with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name): + with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name.removesuffix(".trace")): FreeBootstrapper(bootstrap_config=free_bootstrapper_config) diff --git a/tests/test_litestar_bootstrap.py b/tests/test_litestar_bootstrap.py index 693a165..5901273 100644 --- a/tests/test_litestar_bootstrap.py +++ b/tests/test_litestar_bootstrap.py @@ -143,7 +143,7 @@ def test_litestar_bootstrapper_not_ready() -> None: @pytest.mark.parametrize( "package_name", [ - "opentelemetry", + "opentelemetry.trace", "sentry_sdk", "structlog", "prometheus_client", @@ -152,7 +152,7 @@ def test_litestar_bootstrapper_not_ready() -> None: def test_litestar_bootstrapper_with_missing_instrument_dependency( litestar_config: LitestarConfig, package_name: str ) -> None: - with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name): + with emulate_package_missing(package_name), pytest.warns(UserWarning, match=package_name.removesuffix(".trace")): LitestarBootstrapper(bootstrap_config=litestar_config)