python3-commons 0.2.6__tar.gz → 0.13.5__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.
- python3_commons-0.13.5/.devcontainer/Dockerfile +15 -0
- python3_commons-0.13.5/.devcontainer/devcontainer.json +23 -0
- python3_commons-0.13.5/.devcontainer/docker-compose.yml +17 -0
- python3_commons-0.13.5/.github/workflows/checks.yml +63 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/.github/workflows/python-publish.yaml +17 -9
- python3_commons-0.13.5/.github/workflows/release-on-tag-push.yml +52 -0
- python3_commons-0.13.5/.pre-commit-config.yaml +35 -0
- python3_commons-0.13.5/.python-version +1 -0
- python3_commons-0.13.5/PKG-INFO +30 -0
- python3_commons-0.13.5/pyproject.toml +154 -0
- python3_commons-0.13.5/setup.cfg +4 -0
- python3_commons-0.13.5/src/python3_commons/__init__.py +7 -0
- python3_commons-0.13.5/src/python3_commons/api_client.py +148 -0
- python3_commons-0.13.5/src/python3_commons/audit.py +185 -0
- python3_commons-0.13.5/src/python3_commons/auth.py +48 -0
- python3_commons-0.13.5/src/python3_commons/cache.py +259 -0
- python3_commons-0.13.5/src/python3_commons/conf.py +83 -0
- python3_commons-0.13.5/src/python3_commons/db/__init__.py +88 -0
- python3_commons-0.13.5/src/python3_commons/db/helpers.py +60 -0
- python3_commons-0.13.5/src/python3_commons/db/models/__init__.py +1 -0
- python3_commons-0.13.5/src/python3_commons/db/models/auth.py +11 -0
- python3_commons-0.13.5/src/python3_commons/db/models/common.py +41 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/src/python3_commons/fs.py +2 -2
- python3_commons-0.13.5/src/python3_commons/generators.py +47 -0
- python3_commons-0.13.5/src/python3_commons/helpers.py +139 -0
- python3-commons-0.2.6/src/python3_commons/logging/formatter.py → python3_commons-0.13.5/src/python3_commons/log/formatters.py +6 -0
- python3_commons-0.13.5/src/python3_commons/object_storage.py +189 -0
- python3_commons-0.13.5/src/python3_commons/serializers/common.py +8 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/src/python3_commons/serializers/json.py +6 -8
- python3_commons-0.13.5/src/python3_commons/serializers/msgpack.py +48 -0
- python3_commons-0.13.5/src/python3_commons/serializers/msgspec.py +96 -0
- python3_commons-0.13.5/src/python3_commons.egg-info/PKG-INFO +30 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/src/python3_commons.egg-info/SOURCES.txt +26 -11
- python3_commons-0.13.5/src/python3_commons.egg-info/requires.txt +10 -0
- python3_commons-0.13.5/tests/__init__.py +0 -0
- python3_commons-0.13.5/tests/conftest.py +117 -0
- python3_commons-0.13.5/tests/test_audit.py +31 -0
- python3_commons-0.13.5/tests/test_cache.py +8 -0
- python3_commons-0.13.5/tests/test_helpers.py +10 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/tests/test_msgpack.py +3 -3
- python3_commons-0.13.5/tests/test_msgspec.py +78 -0
- python3_commons-0.13.5/uv.lock +1704 -0
- python3-commons-0.2.6/PKG-INFO +0 -29
- python3-commons-0.2.6/pyproject.toml +0 -3
- python3-commons-0.2.6/requirements.txt +0 -5
- python3-commons-0.2.6/requirements_dev.txt +0 -4
- python3-commons-0.2.6/requirements_test.txt +0 -3
- python3-commons-0.2.6/setup.cfg +0 -87
- python3-commons-0.2.6/setup.py +0 -13
- python3-commons-0.2.6/src/python3_commons/__init__.py +0 -10
- python3-commons-0.2.6/src/python3_commons/conf.py +0 -20
- python3-commons-0.2.6/src/python3_commons/db.py +0 -28
- python3-commons-0.2.6/src/python3_commons/helpers.py +0 -49
- python3-commons-0.2.6/src/python3_commons/minio.py +0 -28
- python3-commons-0.2.6/src/python3_commons/object_storage.py +0 -127
- python3-commons-0.2.6/src/python3_commons/serializers/msgpack.py +0 -62
- python3-commons-0.2.6/src/python3_commons/serializers/msgspec.py +0 -67
- python3-commons-0.2.6/src/python3_commons.egg-info/PKG-INFO +0 -29
- python3-commons-0.2.6/src/python3_commons.egg-info/not-zip-safe +0 -1
- python3-commons-0.2.6/src/python3_commons.egg-info/requires.txt +0 -9
- python3-commons-0.2.6/tests/conftest.py +0 -61
- python3-commons-0.2.6/tests/test_msgspec.py +0 -26
- {python3-commons-0.2.6 → python3_commons-0.13.5}/.coveragerc +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/.gitignore +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/AUTHORS.rst +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/CHANGELOG.rst +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/LICENSE +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/README.md +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/README.rst +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/docs/Makefile +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/docs/_static/.gitignore +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/docs/authors.rst +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/docs/changelog.rst +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/docs/conf.py +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/docs/index.rst +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/docs/license.rst +0 -0
- {python3-commons-0.2.6/src/python3_commons/logging → python3_commons-0.13.5/src/python3_commons/log}/__init__.py +0 -0
- {python3-commons-0.2.6/src/python3_commons/logging → python3_commons-0.13.5/src/python3_commons/log}/filters.py +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/src/python3_commons/serializers/__init__.py +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/src/python3_commons.egg-info/dependency_links.txt +0 -0
- {python3-commons-0.2.6 → python3_commons-0.13.5}/src/python3_commons.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
FROM python:3.13.9-slim-trixie
|
|
2
|
+
|
|
3
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
|
4
|
+
|
|
5
|
+
ENV PYTHONDONTWRITEBYTECODE=1
|
|
6
|
+
ENV PYTHONUNBUFFERED=1
|
|
7
|
+
|
|
8
|
+
RUN apt update && \
|
|
9
|
+
apt install -y curl ca-certificates apt-transport-https wget gnupg && \
|
|
10
|
+
wget -O- https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/postgresql.gpg && \
|
|
11
|
+
echo deb [arch=amd64,arm64,ppc64el signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt/ trixie-pgdg main | tee /etc/apt/sources.list.d/postgresql.list && \
|
|
12
|
+
apt update && \
|
|
13
|
+
apt install -y --no-install-recommends libpq-dev gcc g++ make postgresql-server-dev-18 libffi-dev git cargo pkg-config ssh && \
|
|
14
|
+
apt clean && \
|
|
15
|
+
rm -rf /var/lib/{apt,dpkg,cache,log}/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/postgres
|
|
3
|
+
{
|
|
4
|
+
"name": "python3-commons",
|
|
5
|
+
"dockerComposeFile": "docker-compose.yml",
|
|
6
|
+
"service": "python3-commons",
|
|
7
|
+
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
|
8
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
9
|
+
// "features": {},
|
|
10
|
+
|
|
11
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
12
|
+
// This can be used to network with other containers or the host.
|
|
13
|
+
"forwardPorts": [8080, 5432],
|
|
14
|
+
|
|
15
|
+
"postCreateCommand": "uv sync --all-groups --all-extras",
|
|
16
|
+
"customizations": {
|
|
17
|
+
"jetbrains": {
|
|
18
|
+
"backend": "PyCharm"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
22
|
+
"remoteUser": "root"
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: postgres:18-alpine
|
|
4
|
+
container_name: db
|
|
5
|
+
ports:
|
|
6
|
+
- "5432:5432"
|
|
7
|
+
environment:
|
|
8
|
+
POSTGRES_DB: "${POSTGRES_DB:-python3-commons}"
|
|
9
|
+
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-dev}"
|
|
10
|
+
POSTGRES_USER: "${POSTGRES_USER:-dev}"
|
|
11
|
+
restart: unless-stopped
|
|
12
|
+
|
|
13
|
+
# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
|
|
14
|
+
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
|
15
|
+
|
|
16
|
+
volumes:
|
|
17
|
+
postgres-data:
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
|
|
6
|
+
concurrency:
|
|
7
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
8
|
+
cancel-in-progress: true
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Check Out Repo
|
|
15
|
+
uses: actions/checkout@v5
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v6
|
|
19
|
+
with:
|
|
20
|
+
enable-cache: true
|
|
21
|
+
cache-dependency-glob: "uv.lock"
|
|
22
|
+
|
|
23
|
+
- name: "Set up Python"
|
|
24
|
+
uses: actions/setup-python@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version-file: "pyproject.toml"
|
|
27
|
+
|
|
28
|
+
- name: Install the project
|
|
29
|
+
run: uv sync --locked --all-extras --dev
|
|
30
|
+
|
|
31
|
+
- name: Ruff format check
|
|
32
|
+
run: |
|
|
33
|
+
uvx ruff format --diff ./src/ ./tests/
|
|
34
|
+
|
|
35
|
+
- name: Ruff default rules and import check
|
|
36
|
+
run: |
|
|
37
|
+
uvx ruff check --extend-select I ./src/ ./tests/
|
|
38
|
+
|
|
39
|
+
# - name: Pyright check
|
|
40
|
+
# run: |
|
|
41
|
+
# uvx pyright ./src/
|
|
42
|
+
# tests:
|
|
43
|
+
# runs-on: ubuntu-latest
|
|
44
|
+
# steps:
|
|
45
|
+
# - name: Check Out Repo
|
|
46
|
+
# uses: actions/checkout@v5
|
|
47
|
+
#
|
|
48
|
+
# - name: Install uv
|
|
49
|
+
# uses: astral-sh/setup-uv@v6
|
|
50
|
+
# with:
|
|
51
|
+
# enable-cache: true
|
|
52
|
+
# cache-dependency-glob: "uv.lock"
|
|
53
|
+
#
|
|
54
|
+
# - name: "Set up Python"
|
|
55
|
+
# uses: actions/setup-python@v6
|
|
56
|
+
# with:
|
|
57
|
+
# python-version-file: "pyproject.toml"
|
|
58
|
+
#
|
|
59
|
+
# - name: Install the project
|
|
60
|
+
# run: uv sync --locked --all-extras --all-groups --dev
|
|
61
|
+
#
|
|
62
|
+
# - name: Run unittests
|
|
63
|
+
# run: uv run pytest tests/
|
|
@@ -21,19 +21,27 @@ jobs:
|
|
|
21
21
|
runs-on: ubuntu-latest
|
|
22
22
|
|
|
23
23
|
steps:
|
|
24
|
-
- uses: actions/checkout@
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
- uses: actions/checkout@v5
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v6
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
cache-dependency-glob: "uv.lock"
|
|
31
|
+
|
|
32
|
+
- name: "Set up Python"
|
|
33
|
+
uses: actions/setup-python@v6
|
|
27
34
|
with:
|
|
28
|
-
python-version:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
python-version-file: "pyproject.toml"
|
|
36
|
+
|
|
37
|
+
- name: Install build package
|
|
38
|
+
run: uv pip install --system build setuptools_scm
|
|
39
|
+
|
|
33
40
|
- name: Build package
|
|
34
41
|
run: python -m build
|
|
42
|
+
|
|
35
43
|
- name: Publish package
|
|
36
|
-
uses: pypa/gh-action-pypi-publish@
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
37
45
|
with:
|
|
38
46
|
user: __token__
|
|
39
47
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Create Release on Tag
|
|
2
|
+
permissions:
|
|
3
|
+
contents: write
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags:
|
|
8
|
+
- 'v*'
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
create-release:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
if: ${{ startsWith(github.ref, 'refs/tags') }}
|
|
14
|
+
steps:
|
|
15
|
+
- name: Create GitHub Release
|
|
16
|
+
env:
|
|
17
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
18
|
+
tag: ${{ github.ref_name }}
|
|
19
|
+
run: |
|
|
20
|
+
gh release create "$tag" \
|
|
21
|
+
--repo="$GITHUB_REPOSITORY" \
|
|
22
|
+
--title="v${tag#v}" \
|
|
23
|
+
--generate-notes
|
|
24
|
+
pypi-build-and-push:
|
|
25
|
+
name: Build and push wheel package
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v5
|
|
30
|
+
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v6
|
|
33
|
+
with:
|
|
34
|
+
enable-cache: true
|
|
35
|
+
cache-dependency-glob: "uv.lock"
|
|
36
|
+
|
|
37
|
+
- name: "Set up Python"
|
|
38
|
+
uses: actions/setup-python@v6
|
|
39
|
+
with:
|
|
40
|
+
python-version-file: "pyproject.toml"
|
|
41
|
+
|
|
42
|
+
- name: Install build package
|
|
43
|
+
run: uv pip install --system build setuptools_scm
|
|
44
|
+
|
|
45
|
+
- name: Build package
|
|
46
|
+
run: python -m build
|
|
47
|
+
|
|
48
|
+
- name: Publish package
|
|
49
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
50
|
+
with:
|
|
51
|
+
user: __token__
|
|
52
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
3
|
+
rev: 0.9.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: uv-lock
|
|
6
|
+
# - id: uv-export
|
|
7
|
+
|
|
8
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
9
|
+
rev: v0.14.4
|
|
10
|
+
hooks:
|
|
11
|
+
# Run the linter.
|
|
12
|
+
- id: ruff-check
|
|
13
|
+
args: [ --fix ]
|
|
14
|
+
files: ^(migrations/|src/|tests/).*
|
|
15
|
+
types_or: [ python, pyi ]
|
|
16
|
+
# Run the import sort.
|
|
17
|
+
- id: ruff-check
|
|
18
|
+
args:
|
|
19
|
+
- --select
|
|
20
|
+
- I
|
|
21
|
+
- --fix
|
|
22
|
+
files: ^(migrations/|src/|tests/).*
|
|
23
|
+
types_or: [ python, pyi ]
|
|
24
|
+
# Run the formatter.
|
|
25
|
+
- id: ruff-format
|
|
26
|
+
files: ^(migrations/|src/|tests/).*
|
|
27
|
+
types_or: [ python, pyi ]
|
|
28
|
+
|
|
29
|
+
# - repo: local
|
|
30
|
+
# hooks:
|
|
31
|
+
# - id: pyright
|
|
32
|
+
# name: Run pyright via uvx
|
|
33
|
+
# entry: uvx pyright ./src ./tests
|
|
34
|
+
# language: system
|
|
35
|
+
# pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
>=3.13.9,<3.15.0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python3-commons
|
|
3
|
+
Version: 0.13.5
|
|
4
|
+
Summary: Re-usable Python3 code
|
|
5
|
+
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
|
+
License-Expression: GPL-3.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/kamikaze/python3-commons
|
|
8
|
+
Project-URL: Documentation, https://github.com/kamikaze/python3-commons/wiki
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Requires-Python: <3.15.0,>=3.13.9
|
|
12
|
+
Description-Content-Type: text/x-rst
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
License-File: AUTHORS.rst
|
|
15
|
+
Requires-Dist: aiobotocore~=2.25.2
|
|
16
|
+
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.12.0
|
|
17
|
+
Requires-Dist: asyncpg~=0.30.0
|
|
18
|
+
Requires-Dist: lxml~=6.0.2
|
|
19
|
+
Requires-Dist: msgpack~=1.1.2
|
|
20
|
+
Requires-Dist: msgspec~=0.19.0
|
|
21
|
+
Requires-Dist: pydantic-settings~=2.12.0
|
|
22
|
+
Requires-Dist: SQLAlchemy[asyncio]~=2.0.44
|
|
23
|
+
Requires-Dist: valkey[libvalkey]~=6.1.1
|
|
24
|
+
Requires-Dist: zeep~=4.3.2
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
Re-usable Python3 code
|
|
28
|
+
======================
|
|
29
|
+
|
|
30
|
+
Some description here
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "wheel", "setuptools_scm"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python3-commons"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Re-usable Python3 code"
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "Oleg Korsak", email = "kamikaze.is.waiting.you@gmail.com"}
|
|
11
|
+
]
|
|
12
|
+
license = "GPL-3.0"
|
|
13
|
+
readme = {file = "README.rst", content-type = "text/x-rst"}
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Programming Language :: Python"
|
|
17
|
+
]
|
|
18
|
+
keywords = []
|
|
19
|
+
requires-python = ">=3.13.9,<3.15.0"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"aiobotocore~=2.25.2",
|
|
22
|
+
"aiohttp[speedups]>=3.12.0,<3.15.0",
|
|
23
|
+
"asyncpg~=0.30.0",
|
|
24
|
+
"lxml~=6.0.2",
|
|
25
|
+
"msgpack~=1.1.2",
|
|
26
|
+
"msgspec~=0.19.0",
|
|
27
|
+
"pydantic-settings~=2.12.0",
|
|
28
|
+
"SQLAlchemy[asyncio]~=2.0.44",
|
|
29
|
+
"valkey[libvalkey]~=6.1.1",
|
|
30
|
+
"zeep~=4.3.2"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"build",
|
|
36
|
+
"pip",
|
|
37
|
+
"pre-commit",
|
|
38
|
+
"pyright",
|
|
39
|
+
"ruff",
|
|
40
|
+
"setuptools",
|
|
41
|
+
"setuptools_scm",
|
|
42
|
+
"types-aiobotocore[s3]",
|
|
43
|
+
"wheel",
|
|
44
|
+
]
|
|
45
|
+
testing = [
|
|
46
|
+
"pytest",
|
|
47
|
+
"pytest-asyncio",
|
|
48
|
+
"pytest-cov",
|
|
49
|
+
"pytest-mock",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[project.urls]
|
|
53
|
+
Homepage = "https://github.com/kamikaze/python3-commons"
|
|
54
|
+
Documentation = "https://github.com/kamikaze/python3-commons/wiki"
|
|
55
|
+
|
|
56
|
+
[tool.setuptools.packages.find]
|
|
57
|
+
where = ["src"]
|
|
58
|
+
exclude = ["tests"]
|
|
59
|
+
|
|
60
|
+
[tool.setuptools_scm]
|
|
61
|
+
|
|
62
|
+
[tool.bdist_wheel]
|
|
63
|
+
universal = true
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 120
|
|
67
|
+
indent-width = 4
|
|
68
|
+
target-version = "py313"
|
|
69
|
+
|
|
70
|
+
[tool.ruff.analyze]
|
|
71
|
+
detect-string-imports = true
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint]
|
|
74
|
+
select = [
|
|
75
|
+
"FAST",
|
|
76
|
+
"YTT",
|
|
77
|
+
# "ANN",
|
|
78
|
+
"I",
|
|
79
|
+
"E",
|
|
80
|
+
"W",
|
|
81
|
+
"PYI",
|
|
82
|
+
|
|
83
|
+
"A",
|
|
84
|
+
# "ARG",
|
|
85
|
+
"ASYNC",
|
|
86
|
+
"B",
|
|
87
|
+
"C4",
|
|
88
|
+
# "DTZ",
|
|
89
|
+
"EM",
|
|
90
|
+
"EXE",
|
|
91
|
+
"F",
|
|
92
|
+
"FA",
|
|
93
|
+
"FBT",
|
|
94
|
+
"FLY",
|
|
95
|
+
"FURB",
|
|
96
|
+
# "G",
|
|
97
|
+
"ICN",
|
|
98
|
+
"INP",
|
|
99
|
+
"ISC",
|
|
100
|
+
"LOG",
|
|
101
|
+
"N",
|
|
102
|
+
"NPY",
|
|
103
|
+
"PD",
|
|
104
|
+
"PERF",
|
|
105
|
+
"PIE",
|
|
106
|
+
"PLC",
|
|
107
|
+
"PLE",
|
|
108
|
+
# "PLR",
|
|
109
|
+
"PLW",
|
|
110
|
+
"PT",
|
|
111
|
+
"PTH",
|
|
112
|
+
"Q",
|
|
113
|
+
"RET",
|
|
114
|
+
"RSE",
|
|
115
|
+
"RUF",
|
|
116
|
+
"S",
|
|
117
|
+
"SIM",
|
|
118
|
+
"SLF",
|
|
119
|
+
"SLOT",
|
|
120
|
+
"T20",
|
|
121
|
+
"TC",
|
|
122
|
+
"TID",
|
|
123
|
+
"TRY",
|
|
124
|
+
"UP",
|
|
125
|
+
]
|
|
126
|
+
ignore = [
|
|
127
|
+
"ASYNC109",
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
[tool.ruff.lint.per-file-ignores]
|
|
131
|
+
"tests/*.py" = ["DTZ", "S101"]
|
|
132
|
+
|
|
133
|
+
[tool.ruff.lint.flake8-quotes]
|
|
134
|
+
docstring-quotes = "double"
|
|
135
|
+
inline-quotes = "single"
|
|
136
|
+
|
|
137
|
+
[tool.ruff.format]
|
|
138
|
+
exclude = ["*.pyi"]
|
|
139
|
+
indent-style = "space"
|
|
140
|
+
quote-style = "single"
|
|
141
|
+
|
|
142
|
+
[tool.pyright]
|
|
143
|
+
venvPath = "."
|
|
144
|
+
venv = ".venv"
|
|
145
|
+
reportMatchNotExhaustive = "error"
|
|
146
|
+
reportUnnecessaryComparison = "error"
|
|
147
|
+
|
|
148
|
+
[tool.pytest.ini_options]
|
|
149
|
+
pythonpath = ["src"]
|
|
150
|
+
asyncio_mode = "strict"
|
|
151
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
152
|
+
addopts = "--cov python3_commons --cov-fail-under=20 --cov-report term-missing --verbose"
|
|
153
|
+
norecursedirs = ["dist", "build", ".tox"]
|
|
154
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import errno
|
|
2
|
+
import logging
|
|
3
|
+
from collections.abc import AsyncGenerator, Mapping, Sequence
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from http import HTTPStatus
|
|
8
|
+
from json import dumps
|
|
9
|
+
from typing import Literal
|
|
10
|
+
from uuid import uuid4
|
|
11
|
+
|
|
12
|
+
from aiohttp import ClientResponse, ClientSession, ClientTimeout, client_exceptions
|
|
13
|
+
from aiohttp.abc import URL
|
|
14
|
+
|
|
15
|
+
from python3_commons import audit
|
|
16
|
+
from python3_commons.conf import s3_settings
|
|
17
|
+
from python3_commons.helpers import request_to_curl
|
|
18
|
+
from python3_commons.serializers.json import CustomJSONEncoder
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def _store_response_for_audit(
|
|
24
|
+
response: ClientResponse, audit_name: str, uri_path: str, method: str, request_id: str
|
|
25
|
+
) -> None:
|
|
26
|
+
response_text = await response.text()
|
|
27
|
+
|
|
28
|
+
if response_text:
|
|
29
|
+
now = datetime.now(tz=UTC)
|
|
30
|
+
date_path = now.strftime('%Y/%m/%d')
|
|
31
|
+
timestamp = now.strftime('%H%M%S_%f')
|
|
32
|
+
|
|
33
|
+
await audit.write_audit_data(
|
|
34
|
+
s3_settings,
|
|
35
|
+
f'{date_path}/{audit_name}/{uri_path}/{method}_{timestamp}_{request_id}_response.txt',
|
|
36
|
+
response_text.encode('utf-8'),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@asynccontextmanager
|
|
41
|
+
async def request(
|
|
42
|
+
client: ClientSession,
|
|
43
|
+
base_url: str,
|
|
44
|
+
uri: str,
|
|
45
|
+
query: Mapping | None = None,
|
|
46
|
+
method: Literal['get', 'post', 'put', 'patch', 'options', 'head', 'delete'] = 'get',
|
|
47
|
+
headers: Mapping | None = None,
|
|
48
|
+
json: Mapping | Sequence | str | None = None,
|
|
49
|
+
data: bytes | None = None,
|
|
50
|
+
timeout: ClientTimeout | Enum | None = None,
|
|
51
|
+
audit_name: str | None = None,
|
|
52
|
+
) -> AsyncGenerator[ClientResponse]:
|
|
53
|
+
now = datetime.now(tz=UTC)
|
|
54
|
+
date_path = now.strftime('%Y/%m/%d')
|
|
55
|
+
timestamp = now.strftime('%H%M%S_%f')
|
|
56
|
+
request_id = str(uuid4())[-12:]
|
|
57
|
+
uri_path = uri.removesuffix('/')
|
|
58
|
+
uri_path = uri_path.removeprefix('/')
|
|
59
|
+
url = f'{u[:-1] if (u := str(base_url)).endswith("/") else u}{uri}'
|
|
60
|
+
|
|
61
|
+
if audit_name:
|
|
62
|
+
curl_request = None
|
|
63
|
+
cookies = client.cookie_jar.filter_cookies(URL(base_url)) if base_url else None
|
|
64
|
+
|
|
65
|
+
if method == 'get':
|
|
66
|
+
if headers or query:
|
|
67
|
+
curl_request = request_to_curl(url=url, query=query, method=method, headers=headers, cookies=cookies)
|
|
68
|
+
else:
|
|
69
|
+
curl_request = request_to_curl(
|
|
70
|
+
url=url, query=query, method=method, headers=headers, cookies=cookies, json=json, data=data
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if curl_request:
|
|
74
|
+
await audit.write_audit_data(
|
|
75
|
+
s3_settings,
|
|
76
|
+
f'{date_path}/{audit_name}/{uri_path}/{method}_{timestamp}_{request_id}_request.txt',
|
|
77
|
+
curl_request.encode('utf-8'),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
client_method = getattr(client, method)
|
|
81
|
+
|
|
82
|
+
logger.debug(f'Requesting {method} {url}')
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
if method == 'get':
|
|
86
|
+
async with client_method(url, params=query, headers=headers, timeout=timeout) as response:
|
|
87
|
+
if audit_name:
|
|
88
|
+
await _store_response_for_audit(response, audit_name, uri_path, method, request_id)
|
|
89
|
+
|
|
90
|
+
if response.ok:
|
|
91
|
+
yield response
|
|
92
|
+
else:
|
|
93
|
+
match response.status:
|
|
94
|
+
case HTTPStatus.UNAUTHORIZED:
|
|
95
|
+
msg = 'Unauthorized'
|
|
96
|
+
|
|
97
|
+
raise PermissionError(msg)
|
|
98
|
+
case HTTPStatus.FORBIDDEN:
|
|
99
|
+
msg = 'Forbidden'
|
|
100
|
+
|
|
101
|
+
raise PermissionError(msg)
|
|
102
|
+
case HTTPStatus.NOT_FOUND:
|
|
103
|
+
msg = 'Not found'
|
|
104
|
+
|
|
105
|
+
raise LookupError(msg)
|
|
106
|
+
case HTTPStatus.BAD_REQUEST:
|
|
107
|
+
msg = 'Bad request'
|
|
108
|
+
|
|
109
|
+
raise ValueError(msg)
|
|
110
|
+
case HTTPStatus.TOO_MANY_REQUESTS:
|
|
111
|
+
msg = 'Too many requests'
|
|
112
|
+
|
|
113
|
+
raise InterruptedError(msg)
|
|
114
|
+
case _:
|
|
115
|
+
response.raise_for_status()
|
|
116
|
+
else:
|
|
117
|
+
if json:
|
|
118
|
+
data = dumps(json, cls=CustomJSONEncoder).encode('utf-8')
|
|
119
|
+
|
|
120
|
+
if headers:
|
|
121
|
+
headers = {**headers, 'Content-Type': 'application/json'}
|
|
122
|
+
else:
|
|
123
|
+
headers = {'Content-Type': 'application/json'}
|
|
124
|
+
|
|
125
|
+
async with client_method(url, params=query, data=data, headers=headers, timeout=timeout) as response:
|
|
126
|
+
if audit_name:
|
|
127
|
+
await _store_response_for_audit(response, audit_name, uri_path, method, request_id)
|
|
128
|
+
|
|
129
|
+
yield response
|
|
130
|
+
except client_exceptions.ClientConnectorError as e:
|
|
131
|
+
msg = 'Cient connection error'
|
|
132
|
+
|
|
133
|
+
raise ConnectionRefusedError(msg) from e
|
|
134
|
+
except client_exceptions.ClientOSError as e:
|
|
135
|
+
if e.errno == errno.EPIPE:
|
|
136
|
+
msg = 'Broken pipe'
|
|
137
|
+
|
|
138
|
+
raise ConnectionResetError(msg) from e
|
|
139
|
+
elif e.errno == errno.ECONNRESET:
|
|
140
|
+
msg = 'Connection reset by peer'
|
|
141
|
+
|
|
142
|
+
raise ConnectionResetError(msg) from e
|
|
143
|
+
|
|
144
|
+
raise
|
|
145
|
+
except client_exceptions.ServerDisconnectedError as e:
|
|
146
|
+
msg = 'Server disconnected'
|
|
147
|
+
|
|
148
|
+
raise ConnectionResetError(msg) from e
|