Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full refactoring. #1

Merged
merged 46 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8ce347f
Refactor bootstrapper base class and add sentry instrument. tests are…
insani7y Jul 12, 2024
6916196
Add opentelemetry]
insani7y Jul 13, 2024
8a95047
Continue implementing opentelemetry instrument
insani7y Jul 13, 2024
a980598
Continue implementing opentelemetry instrument
insani7y Jul 13, 2024
c4deff5
Continue implementing opentelemetry instrument
insani7y Jul 13, 2024
c66448e
Continue implementing opentelemetry instrument
insani7y Jul 13, 2024
105acf1
Continue implementing opentelemetry instrument
insani7y Jul 13, 2024
b4304d2
Continue implementing opentelemetry instrument
insani7y Jul 13, 2024
d318c27
Add logging instrument
insani7y Jul 17, 2024
9ce839c
Add logging instrument
insani7y Jul 17, 2024
fc1863b
Add logging instrument
insani7y Jul 17, 2024
5a81e07
Add logging instrument
insani7y Jul 17, 2024
70ecb38
Add prometheus instrument
insani7y Jul 17, 2024
69b8659
Small interface improvments
insani7y Jul 17, 2024
7e598a0
Small interface improvments
insani7y Jul 17, 2024
6e7c76a
Add teardown to litestar bootstrapper
insani7y Jul 17, 2024
61a2efc
Highly arguable decision
insani7y Jul 20, 2024
954d2dd
Highly arguable decision
insani7y Jul 20, 2024
b4b4f8e
Separate instruments and bootstrapper
insani7y Jul 20, 2024
305aff4
Make instrument box private
insani7y Jul 20, 2024
9814971
Make use_instrument typed
insani7y Jul 20, 2024
82ce504
Implement before and after hooks
insani7y Jul 20, 2024
dbdfcfb
Make is_ready a function
insani7y Jul 20, 2024
c4effbb
Make settings per framework
insani7y Jul 22, 2024
2b3e603
Make settings per framework
insani7y Jul 22, 2024
3eba74c
Add tests to some instruments
insani7y Jul 24, 2024
33fc201
Add tests to some instruments
insani7y Jul 24, 2024
7fb4f69
Add tests to some instruments
insani7y Jul 24, 2024
ad2f37b
Add tests to some instruments
insani7y Jul 24, 2024
f469d6c
Add tests to some instruments
insani7y Jul 24, 2024
5a53da4
Trigger codecov
insani7y Jul 24, 2024
40f161d
Try trigger codecov
insani7y Jul 24, 2024
7286144
Try trigger codecov
insani7y Jul 24, 2024
b6e0495
Try trigger codecov
insani7y Jul 24, 2024
80c4f71
Try trigger codecov
insani7y Jul 24, 2024
11cbca0
Fix comments
insani7y Jul 30, 2024
9b38a52
Fix comments
insani7y Jul 30, 2024
d090096
Add some helpers tests
insani7y Jul 30, 2024
a9e003a
Add some helpers tests
insani7y Jul 30, 2024
98732dd
Small changes
insani7y Jul 30, 2024
c49365c
Added tests for everythin
insani7y Aug 2, 2024
88a5436
Update packages
insani7y Aug 2, 2024
b8b4040
Start writing docs
insani7y Aug 2, 2024
31080a6
Logo draft
Jul 22, 2024
1120db7
Logo update
Jul 22, 2024
6a8e04a
Merge branch 'main' into feature/full-refactor
insani7y Aug 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ on:

jobs:
ci:
uses: community-of-python/community-workflow/.github/workflows/preset.yml@main
uses: community-of-python/community-workflow/.github/workflows/preset.yml@main
secrets: inherit
49 changes: 47 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,51 @@
# microbootstrap
This package helps you create application with all necessary instruments already set up

<img src="./logo.svg" width="300" />
- `sentry`
- `prometheus`
- `opentelemetry`
- `logging`

```python
# settings.py
from microbootstrap import LitestarSettings

To be done...

class YourSettings(LitestarSettings):
# Your settings stored here


settings = YourSettings()


# application.py
import litestar
from microbootstrap.bootstrappers.litestar import LitestarBootstrapper

from your_application.settings import settings

# Litestar application with all instruments ready for use!
application: litestar.Litestar = LitestarBootstrapper(settings).bootstrap()
```

Interested? Let's jump right into it ⚡

## Installation

You can install `microbootstrap` with extra for framework you need.

poetry:

```bash
$ poetry add microbootstrap -E litestar
# or
$ poetry add microbootstrap -E fastapi
```

pip:

```bash
$ pip install microbootsrap[litestar]
# or
$ pip install microbootsrap[fastapi]
```
15 changes: 15 additions & 0 deletions microbootstrap/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from microbootstrap.instruments.logging_instrument import LoggingConfig
from microbootstrap.instruments.opentelemetry_instrument import OpentelemetryConfig
from microbootstrap.instruments.prometheus_instrument import PrometheusConfig
from microbootstrap.instruments.sentry_instrument import SentryConfig
from microbootstrap.settings import LitestarSettings


__all__ = (
"SentryConfig",
"OpentelemetryConfig",
"PrometheusConfig",
"LoggingConfig",
"LitestarBootstrapper",
"LitestarSettings",
)
81 changes: 0 additions & 81 deletions microbootstrap/bootstrap.py

This file was deleted.

80 changes: 80 additions & 0 deletions microbootstrap/bootstrappers/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from __future__ import annotations
import abc
import typing

import typing_extensions

from microbootstrap.helpers import dataclass_to_dict_no_defaults, merge_dataclasses_configs, merge_dict_configs
from microbootstrap.instruments.instrument_box import InstrumentBox
from microbootstrap.settings import SettingsT


if typing.TYPE_CHECKING:
from _typeshed import DataclassInstance

from microbootstrap.instruments.base import Instrument, InstrumentConfigT


ApplicationT = typing.TypeVar("ApplicationT")
DataclassT = typing.TypeVar("DataclassT", bound="DataclassInstance")


class ApplicationBootstrapper(abc.ABC, typing.Generic[SettingsT, ApplicationT, DataclassT]):
application_type: type[ApplicationT]
application_config: DataclassT
__instrument_box: InstrumentBox = InstrumentBox()

def __init__(self, settings: SettingsT) -> None:
self.settings = settings
self.__instrument_box.initialize(self.settings)

def configure_application(
self: typing_extensions.Self,
application_config: DataclassT,
) -> typing_extensions.Self:
self.application_config = merge_dataclasses_configs(self.application_config, application_config)
insani7y marked this conversation as resolved.
Show resolved Hide resolved
return self

def configure_instrument(
self: typing_extensions.Self,
instrument_config: InstrumentConfigT,
) -> typing_extensions.Self:
self.__instrument_box.configure_instrument(instrument_config)
return self

@classmethod
def use_instrument(
cls,
) -> typing.Callable[
[type[Instrument[InstrumentConfigT]]],
type[Instrument[InstrumentConfigT]],
]:
return cls.__instrument_box.extend_instruments

def bootstrap(self: typing_extensions.Self) -> ApplicationT:
resulting_application_config = dataclass_to_dict_no_defaults(self.application_config)
for instrument in self.__instrument_box.instruments:
resulting_application_config = merge_dict_configs(
resulting_application_config,
instrument.bootstrap(),
)

application = self.application_type(
**merge_dict_configs(resulting_application_config, self.bootstrap_before()),
)

for instrument in self.__instrument_box.instruments:
application = instrument.bootstrap_after(application)
return self.bootstrap_after(application)

def bootstrap_before(self: typing_extensions.Self) -> dict[str, typing.Any]:
"""Add some framework-related parameters to final bootstrap result before application creation."""
return {}

def bootstrap_after(self: typing_extensions.Self, application: ApplicationT) -> ApplicationT:
"""Add some framework-related parameters to final bootstrap result after application creation."""
return application

def teardown(self: typing_extensions.Self) -> None:
for instrument in self.__instrument_box.instruments:
instrument.teardown()
82 changes: 82 additions & 0 deletions microbootstrap/bootstrappers/litestar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from __future__ import annotations
import typing

import litestar
import litestar.types
import sentry_sdk
import typing_extensions
from litestar import status_codes
from litestar.config.app import AppConfig as LitestarConfig
from litestar.contrib.opentelemetry.config import OpenTelemetryConfig as LitestarOpentelemetryConfig
from litestar.contrib.prometheus import PrometheusConfig as LitestarPrometheusConfig
from litestar.contrib.prometheus import PrometheusController
from litestar.exceptions.http_exceptions import HTTPException

from microbootstrap.bootstrappers.base import ApplicationBootstrapper
from microbootstrap.instruments.logging_instrument import LoggingInstrument
from microbootstrap.instruments.opentelemetry_instrument import OpentelemetryInstrument
from microbootstrap.instruments.prometheus_instrument import PrometheusInstrument
from microbootstrap.instruments.sentry_instrument import SentryInstrument
from microbootstrap.middlewares.litestar import build_litestar_logging_middleware
from microbootstrap.settings import LitestarSettings


class LitestarBootstrapper(
ApplicationBootstrapper[LitestarSettings, litestar.Litestar, LitestarConfig],
):
application_config = LitestarConfig()
application_type = litestar.Litestar

def bootstrap_before(self: typing_extensions.Self) -> dict[str, typing.Any]:
return {"debug": self.settings.service_debug, "on_shutdown": [self.teardown]}


@LitestarBootstrapper.use_instrument()
class LitestarSentryInstrument(SentryInstrument):
@staticmethod
async def sentry_exception_catcher_hook(
exception: Exception,
_request_scope: litestar.types.Scope,
) -> None:
if (
not isinstance(exception, HTTPException)
or exception.status_code >= status_codes.HTTP_500_INTERNAL_SERVER_ERROR
):
sentry_sdk.capture_exception(exception)

def bootstrap_before(self) -> dict[str, typing.Any]:
return {"after_exception": [self.sentry_exception_catcher_hook]}


@LitestarBootstrapper.use_instrument()
class LitetstarOpentelemetryInstrument(OpentelemetryInstrument):
def bootstrap_before(self) -> dict[str, typing.Any]:
return {
"middleware": [
LitestarOpentelemetryConfig(
tracer_provider=self.tracer_provider,
exclude=self.instrument_config.opentelemetry_exclude_urls,
).middleware,
],
}


@LitestarBootstrapper.use_instrument()
class LitestarLoggingInstrument(LoggingInstrument):
def bootstrap_before(self) -> dict[str, typing.Any]:
return {"middleware": [build_litestar_logging_middleware(self.instrument_config.logging_exclude_endpoints)]}


@LitestarBootstrapper.use_instrument()
class LitestarPrometheusInstrument(PrometheusInstrument):
def bootstrap_before(self) -> dict[str, typing.Any]:
class LitestarPrometheusController(PrometheusController):
path = self.instrument_config.prometheus_metrics_path
openmetrics_format = True

litestar_prometheus_config: typing.Final = LitestarPrometheusConfig(
app_name=self.instrument_config.service_name,
**self.instrument_config.prometheus_additional_params,
)

return {"route_handlers": [LitestarPrometheusController], "middleware": [litestar_prometheus_config.middleware]}
10 changes: 10 additions & 0 deletions microbootstrap/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class MicroBootstrapBaseError(Exception):
"""Base for all exceptions."""


class ConfigMergeError(MicroBootstrapBaseError):
"""Raises when it's impossible to merge configs due to type mismatch."""


class MissingInstrumentError(MicroBootstrapBaseError):
"""Raises when attempting to configure instrument, that is not supported yet."""
Loading
Loading