From 6028a945d3780be8575b0a8f7b68807f40337389 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Fri, 21 Mar 2025 13:13:59 +0300 Subject: [PATCH 1/2] remove precommit hooks tasks --- Justfile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Justfile b/Justfile index a7b6336..9f5c2bc 100644 --- a/Justfile +++ b/Justfile @@ -21,9 +21,3 @@ publish: rm -rf dist uv build uv publish --token $PYPI_TOKEN - -hook: - uv run pre-commit install - -unhook: - uv run pre-commit uninstall From f298635e0a29151c21b57d085979292e9510e754 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Fri, 21 Mar 2025 13:31:29 +0300 Subject: [PATCH 2/2] add unbound free bootstrap --- .../bootstraps/fastapi_bootstrap.py | 77 +++++++++++++++++++ .../bootstraps/fastapi_bootstrap/__init__.py | 33 -------- .../healthchecks_instrument.py | 30 -------- .../fastapi_bootstrap/logging_instrument.py | 7 -- .../opentelemetry_instrument.py | 30 -------- .../fastapi_bootstrap/sentry_instrument.py | 7 -- lite_bootstrap/bootstraps/free_bootstrap.py | 15 ++++ tests/test_free_bootstrap.py | 31 ++++++++ 8 files changed, 123 insertions(+), 107 deletions(-) create mode 100644 lite_bootstrap/bootstraps/fastapi_bootstrap.py delete mode 100644 lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py delete mode 100644 lite_bootstrap/bootstraps/fastapi_bootstrap/healthchecks_instrument.py delete mode 100644 lite_bootstrap/bootstraps/fastapi_bootstrap/logging_instrument.py delete mode 100644 lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py delete mode 100644 lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py create mode 100644 lite_bootstrap/bootstraps/free_bootstrap.py create mode 100644 tests/test_free_bootstrap.py diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap.py b/lite_bootstrap/bootstraps/fastapi_bootstrap.py new file mode 100644 index 0000000..5136821 --- /dev/null +++ b/lite_bootstrap/bootstraps/fastapi_bootstrap.py @@ -0,0 +1,77 @@ +import contextlib +import dataclasses +import typing + +from lite_bootstrap.bootstraps.base import BaseBootstrap +from lite_bootstrap.instruments.healthchecks_instrument import HealthChecksInstrument, HealthCheckTypedDict +from lite_bootstrap.instruments.logging_instrument import LoggingInstrument +from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument +from lite_bootstrap.instruments.sentry_instrument import SentryInstrument +from lite_bootstrap.service_config import ServiceConfig + + +with contextlib.suppress(ImportError): + import fastapi + from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor + from opentelemetry.trace import get_tracer_provider + + +@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) +class FastAPIHealthChecksInstrument(HealthChecksInstrument): + enabled: bool = True + path: str = "/health/" + include_in_schema: bool = False + + def build_fastapi_health_check_router(self, service_config: ServiceConfig) -> fastapi.APIRouter: + fastapi_router = fastapi.APIRouter( + tags=["probes"], + include_in_schema=self.include_in_schema, + ) + + @fastapi_router.get(self.path) + async def health_check_handler() -> HealthCheckTypedDict: + return self.render_health_check_data(service_config) + + return fastapi_router + + def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None: + if application: + application.include_router(self.build_fastapi_health_check_router(service_config)) + + +@dataclasses.dataclass(kw_only=True, frozen=True) +class FastAPILoggingInstrument(LoggingInstrument): ... + + +@dataclasses.dataclass(kw_only=True, frozen=True) +class FastAPIOpenTelemetryInstrument(OpenTelemetryInstrument): + excluded_urls: list[str] = dataclasses.field(default_factory=list) + + def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None: + super().bootstrap(service_config, application) + FastAPIInstrumentor.instrument_app( + app=application, + tracer_provider=get_tracer_provider(), + excluded_urls=",".join(self.excluded_urls), + ) + + def teardown(self, application: fastapi.FastAPI | None = None) -> None: + if application: + FastAPIInstrumentor.uninstrument_app(application) + super().teardown() + + +@dataclasses.dataclass(kw_only=True, frozen=True) +class FastAPISentryInstrument(SentryInstrument): ... + + +@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) +class FastAPIBootstrap(BaseBootstrap[fastapi.FastAPI]): + application: fastapi.FastAPI + instruments: typing.Sequence[ + FastAPIOpenTelemetryInstrument + | FastAPISentryInstrument + | FastAPIHealthChecksInstrument + | FastAPILoggingInstrument + ] + service_config: ServiceConfig diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py deleted file mode 100644 index 6911a1c..0000000 --- a/lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -import dataclasses -import typing - -import fastapi - -from lite_bootstrap.bootstraps.base import BaseBootstrap -from lite_bootstrap.bootstraps.fastapi_bootstrap.healthchecks_instrument import FastAPIHealthChecksInstrument -from lite_bootstrap.bootstraps.fastapi_bootstrap.logging_instrument import FastAPILoggingInstrument -from lite_bootstrap.bootstraps.fastapi_bootstrap.opentelemetry_instrument import FastAPIOpenTelemetryInstrument -from lite_bootstrap.bootstraps.fastapi_bootstrap.sentry_instrument import FastAPISentryInstrument - - -__all__ = [ - "FastAPIBootstrap", - "FastAPIHealthChecksInstrument", - "FastAPILoggingInstrument", - "FastAPIOpenTelemetryInstrument", - "FastAPISentryInstrument", -] - -from lite_bootstrap.service_config import ServiceConfig - - -@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) -class FastAPIBootstrap(BaseBootstrap[fastapi.FastAPI]): - application: fastapi.FastAPI - instruments: typing.Sequence[ - FastAPIOpenTelemetryInstrument - | FastAPISentryInstrument - | FastAPIHealthChecksInstrument - | FastAPILoggingInstrument - ] - service_config: ServiceConfig diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/healthchecks_instrument.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/healthchecks_instrument.py deleted file mode 100644 index 0d7215f..0000000 --- a/lite_bootstrap/bootstraps/fastapi_bootstrap/healthchecks_instrument.py +++ /dev/null @@ -1,30 +0,0 @@ -import dataclasses -import typing - -import fastapi - -from lite_bootstrap.instruments.healthchecks_instrument import HealthChecksInstrument, HealthCheckTypedDict -from lite_bootstrap.service_config import ServiceConfig - - -@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) -class FastAPIHealthChecksInstrument(HealthChecksInstrument): - enabled: bool = True - path: str = "/health/" - include_in_schema: bool = False - - def build_fastapi_health_check_router(self, service_config: ServiceConfig) -> fastapi.APIRouter: - fastapi_router: typing.Final = fastapi.APIRouter( - tags=["probes"], - include_in_schema=self.include_in_schema, - ) - - @fastapi_router.get(self.path) - async def health_check_handler() -> HealthCheckTypedDict: - return self.render_health_check_data(service_config) - - return fastapi_router - - def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None: - if application: - application.include_router(self.build_fastapi_health_check_router(service_config)) diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/logging_instrument.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/logging_instrument.py deleted file mode 100644 index e527490..0000000 --- a/lite_bootstrap/bootstraps/fastapi_bootstrap/logging_instrument.py +++ /dev/null @@ -1,7 +0,0 @@ -import dataclasses - -from lite_bootstrap.instruments.logging_instrument import LoggingInstrument - - -@dataclasses.dataclass(kw_only=True, frozen=True) -class FastAPILoggingInstrument(LoggingInstrument): ... diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py deleted file mode 100644 index f22e187..0000000 --- a/lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py +++ /dev/null @@ -1,30 +0,0 @@ -import contextlib -import dataclasses - -import fastapi -from opentelemetry.trace import get_tracer_provider - -from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument -from lite_bootstrap.service_config import ServiceConfig - - -with contextlib.suppress(ImportError): - from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor - - -@dataclasses.dataclass(kw_only=True, frozen=True) -class FastAPIOpenTelemetryInstrument(OpenTelemetryInstrument): - excluded_urls: list[str] = dataclasses.field(default_factory=list) - - def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None: - super().bootstrap(service_config, application) - FastAPIInstrumentor.instrument_app( - app=application, - tracer_provider=get_tracer_provider(), - excluded_urls=",".join(self.excluded_urls), - ) - - def teardown(self, application: fastapi.FastAPI | None = None) -> None: - if application: - FastAPIInstrumentor.uninstrument_app(application) - super().teardown() diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py deleted file mode 100644 index bf127b0..0000000 --- a/lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py +++ /dev/null @@ -1,7 +0,0 @@ -import dataclasses - -from lite_bootstrap.instruments.sentry_instrument import SentryInstrument - - -@dataclasses.dataclass(kw_only=True, frozen=True) -class FastAPISentryInstrument(SentryInstrument): ... diff --git a/lite_bootstrap/bootstraps/free_bootstrap.py b/lite_bootstrap/bootstraps/free_bootstrap.py new file mode 100644 index 0000000..7f79e67 --- /dev/null +++ b/lite_bootstrap/bootstraps/free_bootstrap.py @@ -0,0 +1,15 @@ +import dataclasses +import typing + +from lite_bootstrap.bootstraps.base import BaseBootstrap +from lite_bootstrap.instruments.logging_instrument import LoggingInstrument +from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument +from lite_bootstrap.instruments.sentry_instrument import SentryInstrument +from lite_bootstrap.service_config import ServiceConfig + + +@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) +class FreeBootstrap(BaseBootstrap[None]): + application: None = None + instruments: typing.Sequence[OpenTelemetryInstrument | SentryInstrument | LoggingInstrument] + service_config: ServiceConfig diff --git a/tests/test_free_bootstrap.py b/tests/test_free_bootstrap.py new file mode 100644 index 0000000..391d109 --- /dev/null +++ b/tests/test_free_bootstrap.py @@ -0,0 +1,31 @@ +import structlog +from opentelemetry.sdk.trace.export import ConsoleSpanExporter + +from lite_bootstrap.bootstraps.free_bootstrap import FreeBootstrap +from lite_bootstrap.instruments.logging_instrument import LoggingInstrument +from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument +from lite_bootstrap.instruments.sentry_instrument import SentryInstrument +from lite_bootstrap.service_config import ServiceConfig +from tests.conftest import CustomInstrumentor + + +logger = structlog.getLogger(__name__) + + +def test_free_bootstrap(service_config: ServiceConfig) -> None: + free_bootstrap = FreeBootstrap( + service_config=service_config, + instruments=[ + OpenTelemetryInstrument( + endpoint="otl", + instrumentors=[CustomInstrumentor()], + span_exporter=ConsoleSpanExporter(), + ), + SentryInstrument( + dsn="https://testdsn@localhost/1", + ), + LoggingInstrument(logging_buffer_capacity=0), + ], + ) + free_bootstrap.bootstrap() + logger.info("testing logging", key="value")