probirka 0.3.0a1__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,18 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ trim_trailing_whitespace = true
7
+ insert_final_newline = true
8
+ indent_style = space
9
+ indent_size = 2
10
+
11
+ [*.py]
12
+ indent_size = 4
13
+
14
+ [{Makefile,**.mk}]
15
+ indent_style = tab
16
+
17
+ [*.{md,txt,rst}]
18
+ trim_trailing_whitespace = false
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: pypi
3
+
4
+ on:
5
+ release:
6
+ types: [ created ]
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - name: Extract branch name
14
+ shell: bash
15
+ run: |
16
+ echo "##[set-output name=ver;]$(echo ${GITHUB_REF#refs/*/})"
17
+ id: extract_name_and_version
18
+ - run: sed -i 's/0.0.0/'"${{ steps.extract_name_and_version.outputs.ver }}"'/' pyproject.toml
19
+ - run: head -n 10 pyproject.toml
20
+ - run: sed -i 's/0.0.0/'"${{ steps.extract_name_and_version.outputs.ver }}"'/' probirka/__init__.py
21
+ - uses: astral-sh/setup-uv@v4
22
+ - run: uv python install 3.12
23
+ - run: uv sync --all-extras --dev
24
+ - run: uv build
25
+ - run: uv publish --username __token__ --password ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: tests
3
+
4
+ on:
5
+ pull_request:
6
+ branches: [ main ]
7
+
8
+ jobs:
9
+ tests:
10
+ runs-on: ubuntu-latest
11
+ env:
12
+ SOURCES_DIR: sources
13
+ strategy:
14
+ matrix:
15
+ just-trigger:
16
+ - "lint"
17
+ - "tests"
18
+ python-version:
19
+ - "3.8"
20
+ - "3.9"
21
+ - "3.10"
22
+ - "3.11"
23
+ - "3.12"
24
+ - "3.13"
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: extractions/setup-just@v2
28
+ - uses: astral-sh/setup-uv@v4
29
+ - run: uv python install ${{ matrix.python-version }}
30
+ - run: uv sync --all-extras --dev
31
+ - run: just ${{ matrix.just-trigger }}
@@ -0,0 +1,164 @@
1
+ .python-version
2
+ .idea/
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+ cover/
55
+
56
+ # Translations
57
+ *.mo
58
+ *.pot
59
+
60
+ # Django stuff:
61
+ *.log
62
+ local_settings.py
63
+ db.sqlite3
64
+ db.sqlite3-journal
65
+
66
+ # Flask stuff:
67
+ instance/
68
+ .webassets-cache
69
+
70
+ # Scrapy stuff:
71
+ .scrapy
72
+
73
+ # Sphinx documentation
74
+ docs/_build/
75
+
76
+ # PyBuilder
77
+ .pybuilder/
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ # For a library or package, you might want to ignore these files since the code is
89
+ # intended to run in multiple environments; otherwise, check them in:
90
+ # .python-version
91
+
92
+ # pipenv
93
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
94
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
95
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
96
+ # install all needed dependencies.
97
+ #Pipfile.lock
98
+
99
+ # poetry
100
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
101
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
102
+ # commonly ignored for libraries.
103
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
104
+ #poetry.lock
105
+
106
+ # pdm
107
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
108
+ #pdm.lock
109
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
110
+ # in version control.
111
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
112
+ .pdm.toml
113
+ .pdm-python
114
+ .pdm-build/
115
+
116
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
117
+ __pypackages__/
118
+
119
+ # Celery stuff
120
+ celerybeat-schedule
121
+ celerybeat.pid
122
+
123
+ # SageMath parsed files
124
+ *.sage.py
125
+
126
+ # Environments
127
+ .env
128
+ .venv
129
+ env/
130
+ venv/
131
+ ENV/
132
+ env.bak/
133
+ venv.bak/
134
+
135
+ # Spyder project settings
136
+ .spyderproject
137
+ .spyproject
138
+
139
+ # Rope project settings
140
+ .ropeproject
141
+
142
+ # mkdocs documentation
143
+ /site
144
+
145
+ # mypy
146
+ .mypy_cache/
147
+ .dmypy.json
148
+ dmypy.json
149
+
150
+ # Pyre type checker
151
+ .pyre/
152
+
153
+ # pytype static type analyzer
154
+ .pytype/
155
+
156
+ # Cython debug symbols
157
+ cython_debug/
158
+
159
+ # PyCharm
160
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
161
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
162
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
163
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
164
+ #.idea/
@@ -0,0 +1,31 @@
1
+ ---
2
+ default_install_hook_types:
3
+ - pre-commit
4
+
5
+ repos:
6
+ - repo: local
7
+ hooks:
8
+ - id: mypy
9
+ name: mypy
10
+ entry: python -m mypy
11
+ language: system
12
+ 'types_or': [ python, pyi ]
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: "v0.8.1"
15
+ hooks:
16
+ - id: ruff
17
+ args: [ --fix, --unsafe-fixes ]
18
+ - id: ruff-format
19
+ - repo: https://github.com/pre-commit/pre-commit-hooks
20
+ rev: "v5.0.0"
21
+ hooks:
22
+ - id: check-added-large-files
23
+ - id: check-merge-conflict
24
+ - id: detect-private-key
25
+ - id: check-json
26
+ - id: check-toml
27
+ - id: check-yaml
28
+ - id: double-quote-string-fixer
29
+ - id: end-of-file-fixer
30
+ - id: trailing-whitespace
31
+ exclude: ^.+(\.md)$
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.3
2
+ Name: probirka
3
+ Version: 0.3.0a1
4
+ Summary: Add your description here
5
+ Project-URL: Repository, https://github.com/appKODE/probirka
6
+ Classifier: Development Status :: 5 - Production/Stable
7
+ Classifier: Programming Language :: Python :: 3 :: Only
8
+ Classifier: Programming Language :: Python :: 3.8
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+
18
+ # 🧪: PROBIRKA
19
+
20
+ Async health checks for monitoring purposes
21
+
22
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
23
+ [![PyPI](https://img.shields.io/pypi/v/probirka.svg)](https://pypi.python.org/pypi/probirka)
24
+ [![PyPI](https://img.shields.io/pypi/dm/probirka.svg)](https://pypi.python.org/pypi/probirka)
25
+
26
+ ## Installation
27
+
28
+ ```shell
29
+ pip install probirka
30
+ ```
31
+
32
+ ## Basic usage
33
+
34
+ ```python
35
+ from asyncio import sleep, run
36
+ from dataclasses import asdict
37
+ from pprint import pprint
38
+
39
+ from probirka import Probirka, make_fastapi_endpoint
40
+
41
+ checks = Probirka()
42
+ checks.add_info("project", "example")
43
+ checks.add_info("version", "0.1.0")
44
+
45
+
46
+ @checks.add()
47
+ def ok_check():
48
+ 5 / 1
49
+
50
+
51
+ @checks.add()
52
+ def error_check():
53
+ 5 / 0
54
+
55
+
56
+ @checks.add()
57
+ async def async_check():
58
+ await sleep(1)
59
+
60
+
61
+ async def main():
62
+ results = await checks.run()
63
+ pprint(asdict(results))
64
+
65
+
66
+ if __name__ == "__main__":
67
+ run(main())
68
+
69
+ # {'elapsed': datetime.timedelta(seconds=1, microseconds=1506),
70
+ # 'extra': {'project': 'example', 'version': '0.1.0'},
71
+ # 'ok': False,
72
+ # 'results': [{'elapsed': datetime.timedelta(microseconds=7),
73
+ # 'error': None,
74
+ # 'name': 'ok_check',
75
+ # 'ok': True,
76
+ # 'started_at': datetime.datetime(2024, 6, 4, 23, 1, 53, 471363)},
77
+ # {'elapsed': datetime.timedelta(microseconds=4),
78
+ # 'error': 'division by zero',
79
+ # 'name': 'error_check',
80
+ # 'ok': False,
81
+ # 'started_at': datetime.datetime(2024, 6, 4, 23, 1, 53, 471376)},
82
+ # {'elapsed': datetime.timedelta(seconds=1, microseconds=1259),
83
+ # 'error': None,
84
+ # 'name': 'async_check',
85
+ # 'ok': True,
86
+ # 'started_at': datetime.datetime(2024, 6, 4, 23, 1, 53, 471383)}],
87
+ # 'started_at': datetime.datetime(2024, 6, 4, 23, 1, 53, 471324)}
88
+ ```
89
+
90
+ ## Custom probes
91
+
92
+ ```python
93
+ from asyncio import run
94
+ import os
95
+ from typing import Optional
96
+
97
+ import asyncpg
98
+ from probirka import ProbeBase, Probirka
99
+
100
+
101
+ class PGCheck(
102
+ ProbeBase,
103
+ ):
104
+ def __init__(
105
+ self,
106
+ dsn: str,
107
+ name: Optional[str] = None,
108
+ timeout: Optional[int] = None,
109
+ is_optional: bool = False,
110
+ ) -> None:
111
+ self._dsn = dsn
112
+ super().__init__(
113
+ name=name,
114
+ timeout=timeout,
115
+ is_optional=is_optional,
116
+ )
117
+
118
+ async def _check(
119
+ self,
120
+ ) -> Optional[bool]:
121
+ conn = None
122
+ try:
123
+ conn = await asyncpg.connect(dsn=self._dsn)
124
+ await conn.fetch("SELECT 1")
125
+ finally:
126
+ if conn:
127
+ await conn.close()
128
+ return True
129
+
130
+
131
+ checks = Probirka()
132
+
133
+
134
+ async def main():
135
+ checks.add_probe(
136
+ PGCheck(dsn=os.environ['PG_DSN']),
137
+ )
138
+ results = await checks.run()
139
+ assert results.ok
140
+
141
+
142
+ if __name__ == "__main__":
143
+ run(main())
144
+ ```
145
+
146
+ ## Mask your secrets
147
+
148
+ ```python
149
+ from asyncio import run
150
+ from dataclasses import asdict
151
+ from pprint import pprint
152
+
153
+ from probirka import Probirka, JsonRegularTypes
154
+
155
+
156
+ def mask_sensitive_data(
157
+ name: str,
158
+ value: JsonRegularTypes,
159
+ ) -> JsonRegularTypes:
160
+ if "secret" in name or "secret" in value:
161
+ return "***"
162
+ return value
163
+
164
+
165
+ checks = Probirka(
166
+ mask_sensitive_info_data_cb=mask_sensitive_data,
167
+ )
168
+ checks.add_info("project", "example")
169
+ checks.add_info("version", "0.1.0")
170
+ checks.add_info("top_secret_one", "wow")
171
+ checks.add_info(
172
+ "settings",
173
+ {
174
+ "top_secret_two": 123456,
175
+ "foo": "bar",
176
+ "baz": ["top@secret", "something"],
177
+ },
178
+ )
179
+
180
+
181
+ async def main():
182
+ results = await checks.run()
183
+ pprint(asdict(results))
184
+
185
+
186
+ if __name__ == "__main__":
187
+ run(main())
188
+
189
+
190
+ # {'checks': [],
191
+ # 'info': {'project': 'example',
192
+ # 'settings': {'baz': ['***', 'something'],
193
+ # 'foo': 'bar',
194
+ # 'top_secret_two': '***'},
195
+ # 'top_secret_one': '***',
196
+ # 'version': '0.1.0'},
197
+ # 'ok': True,
198
+ # 'started_at': datetime.datetime(2024, 6, 5, 10, 56, 20, 736558),
199
+ # 'total_elapsed': datetime.timedelta(microseconds=10)}
200
+ ```
201
+
202
+ ## FastAPI endpoint
203
+
204
+ ```python
205
+ from fastapi import FastAPI
206
+ from uvicorn import run
207
+
208
+ from probirka import Probirka, make_fastapi_endpoint
209
+
210
+ app = FastAPI()
211
+
212
+ checks = Probirka()
213
+ checks.add_info("project", "fastapi example")
214
+ checks.add_info("version", "0.1.0")
215
+
216
+
217
+ @checks.add()
218
+ def empty_check():
219
+ pass
220
+
221
+
222
+ if __name__ == "__main__":
223
+ app.add_api_route(
224
+ "/health",
225
+ endpoint=make_fastapi_endpoint(checks, return_results=False),
226
+ )
227
+ app.add_api_route(
228
+ "/self-check",
229
+ endpoint=make_fastapi_endpoint(checks, with_optional=True),
230
+ )
231
+ run(app)
232
+ ```
233
+
234
+
235
+ ## AIOHTTP endpoint
236
+
237
+ ```python
238
+ from aiohttp import web
239
+
240
+ from probirka import Probirka, make_aiohttp_endpoint
241
+
242
+ app = web.Application()
243
+
244
+ checks = Probirka()
245
+ checks.add_info("project", "fastapi example")
246
+ checks.add_info("version", "0.1.0")
247
+
248
+
249
+ @checks.add()
250
+ def empty_check():
251
+ pass
252
+
253
+
254
+ if __name__ == "__main__":
255
+ app.router.add_get(
256
+ "/health",
257
+ handler=make_aiohttp_endpoint(checks, return_results=False),
258
+ )
259
+ app.router.add_get(
260
+ "/self-check",
261
+ handler=make_aiohttp_endpoint(checks, with_optional=True),
262
+ )
263
+ web.run_app(app)
264
+ ```