Skip to content

Commit 9ad2166

Browse files
authored
v3.0.9 (#38)
* v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 * v3.0.9 --------- Co-authored-by: ddc <ddc@users.noreply.github.com>
1 parent a77eef6 commit 9ad2166

72 files changed

Lines changed: 7192 additions & 2533 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/PULL_REQUEST_TEMPLATE

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
<!-- What is this pull request for? Does it fix any issues? -->
44

55
## Checklist
6-
- [ ] If code changes were made, then they have been tested.
7-
- [ ] I have updated the documentation to reflect the changes.
8-
- [ ] I have thought about how this code may affect other services.
9-
- [ ] This PR fixes an issue.
10-
- [ ] This PR add/remove/change unit tests.
11-
- [ ] This PR adds something new (e.g. new method or parameters).
12-
- [ ] This PR is a breaking change (e.g. methods or parameters removed/renamed)
13-
- [ ] This PR is **not** a code change (e.g. documentation, README, ...)
6+
- [ ] If code changes were made, then they have been tested
7+
- [ ] I have updated the documentation to reflect any changes made
8+
- [ ] I have thought about how this code may affect other services
9+
- [ ] This PR fixes an issue
10+
- [ ] This PR is a breaking change (e.g. method, parameters, env variables)
11+
- [ ] This PR adds something new (e.g. method, parameters, env variables)
12+
- [ ] This PR change unit and integration tests
13+
- [ ] This PR is **NOT** a code change (e.g. documentation, packages)
1414

1515
## Reviewer
16-
- [ ] I understand that approving this code, I am also responsible for it going into the codebase.
16+
- [ ] I understand that approving this code, I am also responsible for it going into the codebase

.github/workflows/workflow.yml

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
11
name: CI/CD Pipeline
22

33
'on':
4+
pull_request:
45
push:
5-
branches: ['**']
66
tags: ['v*']
77

8+
env:
9+
LATEST_PYTHON_VERSION: '3.14'
810

911
jobs:
12+
lint:
13+
name: Lint (ruff)
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v6
17+
- uses: astral-sh/ruff-action@v3
18+
with:
19+
args: "check"
20+
1021
test:
1122
name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }}
12-
runs-on: ${{ matrix.runs-on || matrix.os }}
23+
runs-on: ${{ matrix.os }}
24+
needs: lint
1325
strategy:
1426
fail-fast: false
1527
matrix:
16-
os: ['ubuntu-latest', 'macos-latest', 'macos-14-arm64', 'windows-latest']
17-
python-version: ['3.12', '3.13', '3.14']
18-
include:
19-
- os: 'macos-14-arm64'
20-
runs-on: 'macos-14'
28+
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
29+
python-version: ['3.10', '3.14']
2130
steps:
2231
- uses: actions/checkout@v6
2332

2433
- name: Install uv
2534
uses: astral-sh/setup-uv@v7
35+
with:
36+
enable-cache: true
2637

2738
- name: Set up Python ${{ matrix.python-version }}
2839
run: uv python install ${{ matrix.python-version }}
@@ -40,12 +51,12 @@ jobs:
4051
uv venv
4152
if [[ '${{ matrix.os }}' == 'windows-latest' ]]; then
4253
# Skip MySQL on Windows - mysqlclient requires C compilation with MySQL headers
43-
uv pip install -e .[mongodb,mssql,oracle,pgsql,test]
44-
elif [[ '${{ matrix.os }}' == macos* ]]; then
54+
uv sync --locked --all-extras --dev --no-install-package mysqlclient --no-install-package aiomysql
55+
elif [[ '${{ matrix.os }}' == 'macos-latest' ]]; then
4556
export PKG_CONFIG_PATH="$(brew --prefix mysql-client)/lib/pkgconfig"
46-
uv pip install -e .[all,test]
57+
uv sync --locked --all-extras --dev
4758
else
48-
uv pip install -e .[all,test]
59+
uv sync --locked --all-extras --dev
4960
fi
5061
shell: bash
5162

@@ -54,37 +65,39 @@ jobs:
5465
with:
5566
timeout_minutes: 2
5667
max_attempts: 3
57-
command: uv run pytest tests/unit
68+
command: uv run --no-sync pytest tests/unit
5869
shell: bash
5970

6071
- name: Upload coverage to Codecov
61-
if: matrix.python-version == '3.14' && matrix.os == 'ubuntu-latest'
72+
if: matrix.python-version == env.LATEST_PYTHON_VERSION && matrix.os == 'ubuntu-latest'
6273
uses: codecov/codecov-action@v5
6374

6475
- name: Upload test results to Codecov
65-
if: matrix.python-version == '3.14' && matrix.os == 'ubuntu-latest'
76+
if: matrix.python-version == env.LATEST_PYTHON_VERSION && matrix.os == 'ubuntu-latest'
6677
uses: codecov/codecov-action@v5
6778
with:
6879
report_type: test_results
6980

70-
7181
integration-test:
7282
name: Integration Tests
7383
runs-on: ubuntu-latest
84+
needs: lint
7485
steps:
7586
- uses: actions/checkout@v6
7687

7788
- name: Install uv
7889
uses: astral-sh/setup-uv@v7
90+
with:
91+
enable-cache: true
7992

80-
- name: Set up Python 3.14
81-
run: uv python install 3.14
93+
- name: Set up Python ${{ env.LATEST_PYTHON_VERSION }}
94+
run: uv python install ${{ env.LATEST_PYTHON_VERSION }}
8295

8396
- name: Install MySQL client libraries
8497
run: sudo apt-get update && sudo apt-get install -y default-libmysqlclient-dev pkg-config
8598

8699
- name: Install dependencies
87-
run: uv sync --all-extras
100+
run: uv sync --locked --all-extras --dev
88101
shell: bash
89102

90103
- name: Install ODBC driver for MSSQL
@@ -102,11 +115,11 @@ jobs:
102115
with:
103116
timeout_minutes: 3
104117
max_attempts: 3
105-
command: uv run pytest tests/integration --no-cov
118+
command: uv run --no-sync pytest tests/integration --no-cov
106119
shell: bash
107120

108121
build:
109-
name: Build package
122+
name: Build Package
110123
runs-on: ubuntu-latest
111124
needs: [test, integration-test]
112125
if: startsWith(github.ref, 'refs/tags/v')
@@ -115,25 +128,32 @@ jobs:
115128

116129
- name: Install uv
117130
uses: astral-sh/setup-uv@v7
131+
with:
132+
enable-cache: true
118133

119-
- name: Set up Python 3.14
120-
run: uv python install 3.14
134+
- name: Set up Python ${{ env.LATEST_PYTHON_VERSION }}
135+
run: uv python install ${{ env.LATEST_PYTHON_VERSION }}
121136

122137
- name: Build package
123138
run: uv build
124139

140+
- name: Smoke test (wheel)
141+
run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
142+
143+
- name: Smoke test (sdist)
144+
run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
145+
125146
- name: Upload artifacts
126147
uses: actions/upload-artifact@v6
127148
with:
128149
name: python-packages
129150
path: dist/
130151
retention-days: 7
131152

132-
133153
release:
134154
name: Create Release
135155
runs-on: ubuntu-latest
136-
needs: [build]
156+
needs: build
137157
if: startsWith(github.ref, 'refs/tags/v')
138158
permissions:
139159
contents: write
@@ -154,7 +174,6 @@ jobs:
154174
prerelease: false
155175
files: release-assets/*
156176

157-
158177
publish:
159178
name: Publish to PyPI
160179
runs-on: ubuntu-latest

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ cython_debug/
155155
# PyCharm
156156
.idea/
157157

158+
# ds store
159+
**/.DS_Store
160+
158161
# Custom
159-
/junit.xml
160162
*.prof
163+
junit.xml

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/uv-pre-commit
3+
rev: 0.10.0
4+
hooks:
5+
- id: uv-lock

README.md

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<a href="https://www.paypal.com/ncp/payment/6G9Z78QHUD4RJ"><img src="https://img.shields.io/badge/Donate-PayPal-brightgreen.svg?style=plastic" alt="Donate"/></a>
99
<a href="https://github.com/sponsors/ddc"><img src="https://img.shields.io/static/v1?style=plastic&label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=ff69b4" alt="Sponsor"/></a>
1010
<br>
11+
<a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=plastic" alt="Code style: black"/></a>
1112
<a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json?style=plastic" alt="uv"/></a>
1213
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json?style=plastic" alt="Ruff"/></a>
13-
<a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=plastic" alt="Code style: black"/></a>
1414
<br>
1515
<a href="https://www.python.org/downloads"><img src="https://img.shields.io/pypi/pyversions/ddcDatabases.svg?style=plastic&logo=python&cacheSeconds=3600" alt="Python"/></a>
1616
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=plastic" alt="License: MIT"/></a>
@@ -49,7 +49,9 @@
4949
- [Available Methods](#available-methods)
5050
- [Logging](#logging)
5151
- [Development](#development)
52-
- [Create DEV Environment, Running Tests and Building Wheel](#create-dev-environment-running-tests-and-building-wheel)
52+
- [Create DEV Environment and Running Tests](#create-dev-environment-and-running-tests)
53+
- [Update DEV Environment Packages](#update-dev-environment-packages)
54+
- [Building Wheel](#building-wheel)
5355
- [Optionals](#optionals)
5456
- [License](#license)
5557
- [Support](#support)
@@ -91,8 +93,8 @@ Database classes use structured configuration dataclasses instead of flat keywor
9193
|------------------------------|---------------------------------|-------------------------------------------------------------------------------------|
9294
| `{DB}PoolConfig` | Connection pool settings | `pool_size`, `max_overflow`, `pool_recycle`, `connection_timeout` |
9395
| `{DB}SessionConfig` | SQLAlchemy session settings | `echo`, `autoflush`, `expire_on_commit`, `autocommit` |
94-
| `{DB}ConnRetryConfig` | Connection-level retry settings | `enable_retry`, `max_retries`, `initial_retry_delay`, `max_retry_delay` |
95-
| `{DB}OpRetryConfig` | Operation-level retry settings | `enable_retry`, `max_retries`, `initial_retry_delay`, `max_retry_delay`, `jitter` |
96+
| `{DB}ConnectionRetryConfig` | Connection-level retry settings | `enable_retry`, `max_retries`, `initial_retry_delay`, `max_retry_delay` |
97+
| `{DB}OperationRetryConfig` | Operation-level retry settings | `enable_retry`, `max_retries`, `initial_retry_delay`, `max_retry_delay`, `jitter` |
9698
| `PersistentConnectionConfig` | Persistent connection settings | `idle_timeout`, `health_check_interval`, `auto_reconnect` |
9799

98100
**Note:** Replace `{DB}` with the database prefix: `PostgreSQL`, `MySQL`, `MSSQL`, `Oracle`, `MongoDB`, or `Sqlite`.
@@ -120,10 +122,10 @@ Retry with exponential backoff is enabled by default at two levels:
120122

121123
**1. Connection Level** - Retries when establishing database connections:
122124
```python
123-
from ddcDatabases import PostgreSQL, PostgreSQLConnRetryConfig
125+
from ddcDatabases import PostgreSQL, PostgreSQLConnectionRetryConfig
124126

125127
with PostgreSQL(
126-
conn_retry_config=PostgreSQLConnRetryConfig(
128+
connection_retry_config=PostgreSQLConnectionRetryConfig(
127129
enable_retry=True, # Enable/disable retry (default: True)
128130
max_retries=3, # Maximum retry attempts (default: 3)
129131
initial_retry_delay=1.0, # Initial delay in seconds (default: 1.0)
@@ -136,10 +138,10 @@ with PostgreSQL(
136138

137139
**2. Operation Level** - Retries individual database operations (fetchall, insert, etc.):
138140
```python
139-
from ddcDatabases import DBUtils, PostgreSQL, PostgreSQLOpRetryConfig
141+
from ddcDatabases import DBUtils, PostgreSQL, PostgreSQLOperationRetryConfig
140142

141143
with PostgreSQL(
142-
op_retry_config=PostgreSQLOpRetryConfig(
144+
operation_retry_config=PostgreSQLOperationRetryConfig(
143145
enable_retry=True, # Enable/disable (default: True)
144146
max_retries=3, # Max attempts (default: 3)
145147
initial_retry_delay=1.0, # Initial delay in seconds (default: 1.0)
@@ -174,6 +176,8 @@ from ddcDatabases import (
174176
MySQLPersistent,
175177
MongoDBPersistent,
176178
PersistentConnectionConfig,
179+
PostgreSQLConnectionRetryConfig,
180+
PostgreSQLOperationRetryConfig,
177181
close_all_persistent_connections,
178182
)
179183

@@ -188,6 +192,19 @@ conn = PostgreSQLPersistent(
188192
health_check_interval=30, # seconds between health checks (default: 30)
189193
auto_reconnect=True, # auto-reconnect on failure (default: True)
190194
),
195+
connection_retry_config=PostgreSQLConnectionRetryConfig(
196+
enable_retry=True, # enable connection retry (default: True)
197+
max_retries=5, # max connection attempts (default: 5)
198+
initial_retry_delay=1.0, # initial delay in seconds (default: 1.0)
199+
max_retry_delay=30.0, # max delay in seconds (default: 30.0)
200+
),
201+
operation_retry_config=PostgreSQLOperationRetryConfig(
202+
enable_retry=True, # enable operation retry (default: True)
203+
max_retries=3, # max operation attempts (default: 3)
204+
initial_retry_delay=0.5, # initial delay in seconds (default: 0.5)
205+
max_retry_delay=10.0, # max delay in seconds (default: 10.0)
206+
jitter=0.1, # randomization factor (default: 0.1)
207+
),
191208
)
192209

193210
# Use as context manager (doesn't disconnect on exit, just updates last-used time)
@@ -208,6 +225,36 @@ async with conn as session:
208225
close_all_persistent_connections()
209226
```
210227

228+
### Execute with Retry
229+
230+
The `execute_with_retry` method provides automatic session management with retry logic:
231+
232+
**Synchronous:**
233+
```python
234+
from ddcDatabases import PostgreSQLPersistent
235+
236+
db = PostgreSQLPersistent(logger=logger)
237+
result = db.execute_with_retry(
238+
lambda session: MyDal(session).do_something()
239+
)
240+
```
241+
242+
**Asynchronous:**
243+
```python
244+
from ddcDatabases import PostgreSQLPersistent
245+
246+
db = PostgreSQLPersistent(async_mode=True, logger=logger)
247+
result = await db.execute_with_retry(
248+
lambda session: MyDal(session).do_something()
249+
)
250+
```
251+
252+
The method automatically:
253+
- Connects (or reuses existing connection)
254+
- Executes the operation with the session
255+
- Commits on success, rolls back on failure
256+
- Retries with exponential backoff if `auto_reconnect` is enabled
257+
211258
**Available Persistent Connection Classes:**
212259

213260
- `PostgreSQLPersistent` - PostgreSQL (sync/async)
@@ -605,7 +652,7 @@ async with PostgreSQL() as session:
605652
results = await db_utils_async.fetchall(stmt)
606653
```
607654

608-
**Note:** Retry logic is configured at the database connection level using `op_retry_config` (see [Retry Logic](#retry-logic) section).
655+
**Note:** Retry logic is configured at the database connection level using `operation_retry_config` (see [Retry Logic](#retry-logic) section).
609656

610657

611658
# Logging
@@ -638,27 +685,41 @@ logging.getLogger("ddcDatabases").addHandler(logging.StreamHandler())
638685

639686
# Development
640687

641-
Must have [UV](https://uv.run/docs/getting-started/installation),
642-
[Black](https://black.readthedocs.io/en/stable/getting_started.html),
643-
[Ruff](https://docs.astral.sh/ruff/installation/), and
644-
[Poe the Poet](https://poethepoet.naber.dev/installation) installed.
688+
Must have [UV](https://uv.run/docs/getting-started/installation) installed.
689+
690+
## Create DEV Environment and Running Tests
645691

646-
## Create DEV Environment, Running Tests and Building Wheel
692+
> **Note:** All poe tasks automatically run ruff linter along with Black formatting
647693
648694
```shell
649-
uv sync --all-extras
650-
poe linter
695+
uv sync --all-extras --all-groups
651696
poe test
652697
poe test-integration
698+
```
699+
700+
## Update DEV Environment Packages
701+
This will update all packages dependencies
702+
703+
```shell
704+
poe updatedev
705+
```
706+
707+
708+
## Building Wheel
709+
This will update all packages, run linter, both unit and integration tests and finally build the wheel
710+
711+
```shell
653712
poe build
654713
```
655714

656715
## Optionals
657716

658717
```shell
659-
poe profile (create a cprofile_unit.prof file from unit tests)
660-
poe profile-integration (create a cprofile_integration.prof file from integration tests)
661-
```
718+
# create a cprofile_unit.prof file from unit tests
719+
poe profile
720+
# create a cprofile_integration.prof file from integration tests
721+
poe profile-integration
722+
`````
662723

663724

664725
# License

0 commit comments

Comments
 (0)