lite-bootstrap 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,49 @@
1
+ name: main
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request: {}
8
+
9
+ concurrency:
10
+ group: ${{ github.head_ref || github.run_id }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ lint:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v3
18
+ - uses: actions/setup-python@v4
19
+ with:
20
+ python-version: "3.10"
21
+ - uses: extractions/setup-just@v2
22
+ - run: curl -LsSf https://astral.sh/uv/install.sh | sh
23
+ - run: just install lint-ci
24
+
25
+ pytest:
26
+ runs-on: ubuntu-latest
27
+ strategy:
28
+ fail-fast: false
29
+ matrix:
30
+ python-version:
31
+ - "3.10"
32
+ - "3.11"
33
+ - "3.12"
34
+ steps:
35
+ - uses: actions/checkout@v3
36
+ - uses: actions/setup-python@v4
37
+ with:
38
+ python-version: ${{ matrix.python-version }}
39
+ - uses: extractions/setup-just@v2
40
+ - run: curl -LsSf https://astral.sh/uv/install.sh | sh
41
+ - run: just install test . --cov=. --cov-report xml
42
+ - name: Upload coverage to Codecov
43
+ uses: codecov/codecov-action@v4.0.1
44
+ env:
45
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
46
+ with:
47
+ files: ./coverage.xml
48
+ flags: unittests
49
+ name: codecov-${{ matrix.python-version }}
@@ -0,0 +1,24 @@
1
+ name: Publish Package
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.12"
16
+ - uses: actions/cache@v4
17
+ with:
18
+ path: ~/.cache/uv
19
+ key: publish-${{ hashFiles('pyproject.toml') }}
20
+ - uses: extractions/setup-just@v2
21
+ - run: curl -LsSf https://astral.sh/uv/install.sh | sh
22
+ - run: just publish
23
+ env:
24
+ PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,22 @@
1
+ # Generic things
2
+ *.pyc
3
+ *~
4
+ __pycache__/*
5
+ *.swp
6
+ *.sqlite3
7
+ *.map
8
+ .vscode
9
+ .idea
10
+ .DS_Store
11
+ .env
12
+ .mypy_cache
13
+ .pytest_cache
14
+ .ruff_cache
15
+ .coverage
16
+ htmlcov/
17
+ coverage.xml
18
+ pytest.xml
19
+ dist/
20
+ .python-version
21
+ .venv
22
+ uv.lock
@@ -0,0 +1,24 @@
1
+ default: install lint test
2
+
3
+ install:
4
+ uv lock --upgrade
5
+ uv sync --all-extras --frozen
6
+
7
+ lint:
8
+ uv run ruff format .
9
+ uv run ruff check . --fix
10
+ uv run mypy .
11
+
12
+ lint-ci:
13
+ uv run ruff format . --check
14
+ uv run ruff check . --no-fix
15
+ uv run mypy .
16
+
17
+ test *args:
18
+ uv run pytest {{ args }}
19
+
20
+ publish:
21
+ rm -rf dist/*
22
+ uv tool run --from build python -m build --installer uv
23
+ uv tool run twine check dist/*
24
+ uv tool run twine upload dist/* --username __token__ --password $PYPI_TOKEN
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.3
2
+ Name: lite-bootstrap
3
+ Version: 0.1.0
4
+ Summary: Lite package for bootstrapping new microservices
5
+ Project-URL: repository, https://github.com/modern-python/lite-bootstrap
6
+ Author-email: Artur Shiriev <me@shiriev.ru>
7
+ License-Expression: MIT
8
+ Keywords: bootstrap,error-tracing,fastapi,microservice,opentelemetry,python,sentry
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Software Development :: Libraries
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: <4,>=3.10
15
+ Provides-Extra: fastapi
16
+ Requires-Dist: fastapi; extra == 'fastapi'
17
+ Requires-Dist: opentelemetry-api; extra == 'fastapi'
18
+ Requires-Dist: opentelemetry-exporter-otlp; extra == 'fastapi'
19
+ Requires-Dist: opentelemetry-instrumentation; extra == 'fastapi'
20
+ Requires-Dist: opentelemetry-instrumentation-fastapi; extra == 'fastapi'
21
+ Requires-Dist: opentelemetry-sdk; extra == 'fastapi'
22
+ Requires-Dist: sentry-sdk; extra == 'fastapi'
23
+ Provides-Extra: tracing
24
+ Requires-Dist: opentelemetry-api; extra == 'tracing'
25
+ Requires-Dist: opentelemetry-exporter-otlp; extra == 'tracing'
26
+ Requires-Dist: opentelemetry-instrumentation; extra == 'tracing'
27
+ Requires-Dist: opentelemetry-sdk; extra == 'tracing'
28
+ Requires-Dist: sentry-sdk; extra == 'tracing'
29
+ Description-Content-Type: text/markdown
30
+
31
+ Lite-Bootstrap package
32
+ ==
33
+ [![Test Coverage](https://codecov.io/gh/modern-python/lite-bootstrap/branch/main/graph/badge.svg)](https://codecov.io/gh/modern-python/lite-bootstrap)
34
+ [![MyPy Strict](https://img.shields.io/badge/mypy-strict-blue)](https://mypy.readthedocs.io/en/stable/getting_started.html#strict-mode-and-configuration)
35
+ [![Supported versions](https://img.shields.io/pypi/pyversions/lite-bootstrap.svg)](https://pypi.python.org/pypi/lite-bootstrap)
36
+ [![downloads](https://img.shields.io/pypi/dm/lite-bootstrap.svg)](https://pypistats.org/packages/lite-bootstrap)
37
+ [![GitHub stars](https://img.shields.io/github/stars/modern-python/lite-bootstrap)](https://github.com/modern-python/lite-bootstrap/stargazers)
38
+
39
+ This package helps to build new microservices
40
+
41
+ ## Quickstart:
42
+ ### Installation
43
+
44
+ ```shell
45
+ $ pip install lite-bootstrap
46
+ ```
@@ -0,0 +1,16 @@
1
+ Lite-Bootstrap package
2
+ ==
3
+ [![Test Coverage](https://codecov.io/gh/modern-python/lite-bootstrap/branch/main/graph/badge.svg)](https://codecov.io/gh/modern-python/lite-bootstrap)
4
+ [![MyPy Strict](https://img.shields.io/badge/mypy-strict-blue)](https://mypy.readthedocs.io/en/stable/getting_started.html#strict-mode-and-configuration)
5
+ [![Supported versions](https://img.shields.io/pypi/pyversions/lite-bootstrap.svg)](https://pypi.python.org/pypi/lite-bootstrap)
6
+ [![downloads](https://img.shields.io/pypi/dm/lite-bootstrap.svg)](https://pypistats.org/packages/lite-bootstrap)
7
+ [![GitHub stars](https://img.shields.io/github/stars/modern-python/lite-bootstrap)](https://github.com/modern-python/lite-bootstrap/stargazers)
8
+
9
+ This package helps to build new microservices
10
+
11
+ ## Quickstart:
12
+ ### Installation
13
+
14
+ ```shell
15
+ $ pip install lite-bootstrap
16
+ ```
File without changes
@@ -0,0 +1,34 @@
1
+ import dataclasses
2
+
3
+ import fastapi
4
+ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
5
+ from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
6
+
7
+ from lite_bootstrap.opentelemetry_bootstrap import OpenTelemetryBootstrap
8
+ from lite_bootstrap.sentry_bootstrap import SentryBootstrap
9
+
10
+
11
+ @dataclasses.dataclass(kw_only=True, slots=True)
12
+ class FastAPIBootstrap:
13
+ app: fastapi.FastAPI
14
+ opentelemetry: OpenTelemetryBootstrap
15
+ sentry: SentryBootstrap
16
+ opentelemetry_excluded_urls: list[str] = dataclasses.field(default_factory=list)
17
+
18
+ def bootstrap_init(self) -> None:
19
+ if self.sentry.sentry_dsn:
20
+ self.sentry.start_tracing()
21
+ self.app.add_middleware(SentryAsgiMiddleware)
22
+
23
+ self.opentelemetry.start_tracing()
24
+ if self.opentelemetry.endpoint:
25
+ FastAPIInstrumentor.instrument_app(
26
+ app=self.app,
27
+ tracer_provider=self.opentelemetry.tracer_provider,
28
+ excluded_urls=",".join(self.opentelemetry_excluded_urls),
29
+ )
30
+
31
+ def teardown(self) -> None:
32
+ self.opentelemetry.teardown()
33
+ if self.opentelemetry.endpoint:
34
+ FastAPIInstrumentor.uninstrument_app(self.app)
@@ -0,0 +1,46 @@
1
+ import dataclasses
2
+
3
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
4
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore[attr-defined]
5
+ from opentelemetry.sdk import resources
6
+ from opentelemetry.sdk.trace import TracerProvider
7
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
8
+ from opentelemetry.trace import set_tracer_provider
9
+
10
+
11
+ @dataclasses.dataclass(kw_only=True, slots=True)
12
+ class OpenTelemetryBootstrap:
13
+ endpoint: str
14
+ service_name: str
15
+ instruments: list[BaseInstrumentor] = dataclasses.field(default_factory=list)
16
+ tracer_provider: TracerProvider | None = dataclasses.field(init=False)
17
+
18
+ def start_tracing(self) -> None:
19
+ if not self.endpoint:
20
+ return
21
+
22
+ self.tracer_provider: TracerProvider = TracerProvider(
23
+ resource=resources.Resource.create({resources.SERVICE_NAME: self.service_name}),
24
+ )
25
+ self.tracer_provider.add_span_processor(
26
+ BatchSpanProcessor(
27
+ OTLPSpanExporter(
28
+ endpoint=self.endpoint,
29
+ insecure=True,
30
+ ),
31
+ ),
32
+ )
33
+
34
+ for instrument in self.instruments:
35
+ instrument.instrument(
36
+ tracer_provider=self.tracer_provider,
37
+ )
38
+
39
+ set_tracer_provider(self.tracer_provider)
40
+
41
+ def teardown(self) -> None:
42
+ if not self.endpoint:
43
+ return
44
+
45
+ for instrument in self.instruments:
46
+ instrument.uninstrument()
File without changes
@@ -0,0 +1,32 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ import sentry_sdk
5
+
6
+
7
+ @dataclasses.dataclass(kw_only=True, slots=True)
8
+ class SentryBootstrap:
9
+ sentry_dsn: str
10
+ environment: str | None = None
11
+ release: str | None = None
12
+ max_breadcrumbs: int = 15
13
+ attach_stacktrace: bool = True
14
+ default_integrations: bool = True
15
+ sentry_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
16
+ tags: dict[str, str] | None = None
17
+
18
+ def start_tracing(self) -> None:
19
+ if not self.sentry_dsn:
20
+ return
21
+
22
+ sentry_sdk.init(
23
+ dsn=self.sentry_dsn,
24
+ environment=self.environment,
25
+ max_breadcrumbs=self.max_breadcrumbs,
26
+ attach_stacktrace=self.attach_stacktrace,
27
+ default_integrations=self.default_integrations,
28
+ release=self.release,
29
+ **self.sentry_params,
30
+ )
31
+ tags: dict[str, str] = self.tags or {}
32
+ sentry_sdk.set_tags(tags)
@@ -0,0 +1,102 @@
1
+ [project]
2
+ name = "lite-bootstrap"
3
+ description = "Lite package for bootstrapping new microservices"
4
+ authors = [
5
+ { name = "Artur Shiriev", email = "me@shiriev.ru" },
6
+ ]
7
+ readme = "README.md"
8
+ requires-python = ">=3.10,<4"
9
+ license = "MIT"
10
+ keywords = [
11
+ "python",
12
+ "microservice",
13
+ "bootstrap",
14
+ "opentelemetry",
15
+ "sentry",
16
+ "error-tracing",
17
+ "fastapi",
18
+ ]
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Typing :: Typed",
24
+ "Topic :: Software Development :: Libraries",
25
+ ]
26
+ dynamic = ["version"]
27
+ packages = [
28
+ { include = "lite_bootstrap" },
29
+ ]
30
+
31
+ [project.urls]
32
+ repository = "https://github.com/modern-python/lite-bootstrap"
33
+
34
+ [project.optional-dependencies]
35
+ tracing = [
36
+ "sentry-sdk",
37
+ "opentelemetry-api",
38
+ "opentelemetry-sdk",
39
+ "opentelemetry-exporter-otlp",
40
+ "opentelemetry-instrumentation",
41
+ ]
42
+ fastapi = [
43
+ "fastapi",
44
+ "sentry-sdk",
45
+ "opentelemetry-api",
46
+ "opentelemetry-sdk",
47
+ "opentelemetry-exporter-otlp",
48
+ "opentelemetry-instrumentation",
49
+ "opentelemetry-instrumentation-fastapi",
50
+ ]
51
+
52
+ [tool.uv]
53
+ dev-dependencies = [
54
+ "pytest",
55
+ "pytest-cov",
56
+ "httpx", # for test client
57
+ "mypy",
58
+ "ruff",
59
+ ]
60
+
61
+ [build-system]
62
+ requires = ["hatchling", "hatch-vcs"]
63
+ build-backend = "hatchling.build"
64
+
65
+ [tool.hatch.version]
66
+ source = "vcs"
67
+
68
+ [tool.mypy]
69
+ python_version = "3.10"
70
+ strict = true
71
+
72
+ [tool.ruff]
73
+ fix = true
74
+ unsafe-fixes = true
75
+ line-length = 120
76
+ target-version = "py310"
77
+
78
+ [tool.ruff.format]
79
+ docstring-code-format = true
80
+
81
+ [tool.ruff.lint]
82
+ select = ["ALL"]
83
+ ignore = [
84
+ "D1", # allow missing docstrings
85
+ "S101", # allow asserts
86
+ "TCH", # ignore flake8-type-checking
87
+ "FBT", # allow boolean args
88
+ "ANN101", # missing-type-self
89
+ "ANN102", # missing-type-cls
90
+ "D203", # "one-blank-line-before-class" conflicting with D211
91
+ "D213", # "multi-line-summary-second-line" conflicting with D212
92
+ "COM812", # flake8-commas "Trailing comma missing"
93
+ "ISC001", # flake8-implicit-str-concat
94
+ ]
95
+ isort.lines-after-imports = 2
96
+ isort.no-lines-before = ["standard-library", "local-folder"]
97
+
98
+ [tool.pytest.ini_options]
99
+ addopts = "--cov=. --cov-report term-missing"
100
+
101
+ [tool.coverage.report]
102
+ exclude_also = ["if typing.TYPE_CHECKING:"]
File without changes
@@ -0,0 +1,27 @@
1
+ import typing
2
+
3
+ import pytest
4
+ from fastapi import APIRouter, FastAPI
5
+ from fastapi.responses import JSONResponse
6
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore[attr-defined]
7
+
8
+
9
+ class CustomInstrumentor(BaseInstrumentor): # type: ignore[misc]
10
+ def instrumentation_dependencies(self) -> typing.Collection[str]:
11
+ return []
12
+
13
+ def _uninstrument(self, **kwargs: typing.Mapping[str, typing.Any]) -> None:
14
+ pass
15
+
16
+
17
+ @pytest.fixture
18
+ def fastapi_app() -> FastAPI:
19
+ app: typing.Final = FastAPI()
20
+ router: typing.Final = APIRouter()
21
+
22
+ @router.get("/test")
23
+ async def for_test_endpoint() -> JSONResponse:
24
+ return JSONResponse(content={"key": "value"})
25
+
26
+ app.include_router(router)
27
+ return app
@@ -0,0 +1,39 @@
1
+ from fastapi import FastAPI
2
+ from fastapi.testclient import TestClient
3
+ from starlette import status
4
+
5
+ from lite_bootstrap.fastapi_bootstrap import FastAPIBootstrap
6
+ from lite_bootstrap.opentelemetry_bootstrap import OpenTelemetryBootstrap
7
+ from lite_bootstrap.sentry_bootstrap import SentryBootstrap
8
+ from tests.conftest import CustomInstrumentor
9
+
10
+
11
+ def test_fastapi_bootstrap(fastapi_app: FastAPI) -> None:
12
+ fastapi_bootstrap = FastAPIBootstrap(
13
+ app=fastapi_app,
14
+ opentelemetry=OpenTelemetryBootstrap(
15
+ endpoint="localhost",
16
+ service_name="test_service",
17
+ instruments=[CustomInstrumentor()],
18
+ ),
19
+ sentry=SentryBootstrap(sentry_dsn="https://testdsn@test.sentry.com/1"),
20
+ )
21
+ fastapi_bootstrap.bootstrap_init()
22
+ fastapi_bootstrap.teardown()
23
+
24
+
25
+ def test_fastapi_bootstrap_with_request(fastapi_app: FastAPI) -> None:
26
+ fastapi_bootstrap = FastAPIBootstrap(
27
+ app=fastapi_app,
28
+ opentelemetry=OpenTelemetryBootstrap(
29
+ endpoint="",
30
+ service_name="test_service",
31
+ instruments=[CustomInstrumentor()],
32
+ ),
33
+ sentry=SentryBootstrap(sentry_dsn=""),
34
+ )
35
+ fastapi_bootstrap.bootstrap_init()
36
+ response = TestClient(fastapi_app).get("/test")
37
+ assert response.status_code == status.HTTP_200_OK
38
+
39
+ fastapi_bootstrap.teardown()
@@ -0,0 +1,21 @@
1
+ from lite_bootstrap.opentelemetry_bootstrap import OpenTelemetryBootstrap
2
+ from tests.conftest import CustomInstrumentor
3
+
4
+
5
+ def test_bootstrap_opentelemetry() -> None:
6
+ opentelemetry = OpenTelemetryBootstrap(
7
+ endpoint="localhost",
8
+ service_name="test_service",
9
+ instruments=[CustomInstrumentor()],
10
+ )
11
+ opentelemetry.start_tracing()
12
+ opentelemetry.teardown()
13
+
14
+
15
+ def test_bootstrap_opentelemetry_empty_instruments() -> None:
16
+ opentelemetry = OpenTelemetryBootstrap(
17
+ endpoint="localhost",
18
+ service_name="test_service",
19
+ )
20
+ opentelemetry.start_tracing()
21
+ opentelemetry.teardown()
@@ -0,0 +1,9 @@
1
+ from lite_bootstrap.sentry_bootstrap import SentryBootstrap
2
+
3
+
4
+ def test_sentry_bootstrap() -> None:
5
+ SentryBootstrap(sentry_dsn="https://testdsn@test.sentry.com/1", tags={"tag": "value"}).start_tracing()
6
+
7
+
8
+ def test_sentry_bootstrap_empty_dsn() -> None:
9
+ SentryBootstrap(sentry_dsn="").start_tracing()