pgoutput-decoder 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.
- pgoutput_decoder-0.1.0/.github/dependabot.yml +28 -0
- pgoutput_decoder-0.1.0/.github/workflows/build.yml +49 -0
- pgoutput_decoder-0.1.0/.github/workflows/ci.yml +205 -0
- pgoutput_decoder-0.1.0/.github/workflows/release.yml +161 -0
- pgoutput_decoder-0.1.0/.gitignore +29 -0
- pgoutput_decoder-0.1.0/.pre-commit-config.yaml +37 -0
- pgoutput_decoder-0.1.0/AGENTS.md +125 -0
- pgoutput_decoder-0.1.0/Cargo.lock +1470 -0
- pgoutput_decoder-0.1.0/Cargo.toml +38 -0
- pgoutput_decoder-0.1.0/DESIGN.md +20 -0
- pgoutput_decoder-0.1.0/PKG-INFO +1139 -0
- pgoutput_decoder-0.1.0/README.md +1118 -0
- pgoutput_decoder-0.1.0/RELEASE.md +319 -0
- pgoutput_decoder-0.1.0/example_debezium.py +96 -0
- pgoutput_decoder-0.1.0/examples/README.md +323 -0
- pgoutput_decoder-0.1.0/examples/background_thread.py +456 -0
- pgoutput_decoder-0.1.0/examples/basic_cdc.py +57 -0
- pgoutput_decoder-0.1.0/examples/celery_integration.py +401 -0
- pgoutput_decoder-0.1.0/examples/setup_postgres.sql +148 -0
- pgoutput_decoder-0.1.0/examples/sync_wrapper.py +230 -0
- pgoutput_decoder-0.1.0/examples/test_replication.rs +237 -0
- pgoutput_decoder-0.1.0/justfile +275 -0
- pgoutput_decoder-0.1.0/pyproject.toml +57 -0
- pgoutput_decoder-0.1.0/python/pgoutput_decoder/__init__.py +112 -0
- pgoutput_decoder-0.1.0/python/pgoutput_decoder/exceptions.py +25 -0
- pgoutput_decoder-0.1.0/src/lib.rs +31 -0
- pgoutput_decoder-0.1.0/src/pgoutput/decoder.rs +272 -0
- pgoutput_decoder-0.1.0/src/pgoutput/messages.rs +272 -0
- pgoutput_decoder-0.1.0/src/pgoutput/mod.rs +7 -0
- pgoutput_decoder-0.1.0/src/pgoutput/types.rs +200 -0
- pgoutput_decoder-0.1.0/src/replication.rs +479 -0
- pgoutput_decoder-0.1.0/src/utils.rs +65 -0
- pgoutput_decoder-0.1.0/tests/README.md +287 -0
- pgoutput_decoder-0.1.0/tests/TEST_COVERAGE.md +150 -0
- pgoutput_decoder-0.1.0/tests/conftest.py +128 -0
- pgoutput_decoder-0.1.0/tests/test_acknowledgement.py +417 -0
- pgoutput_decoder-0.1.0/tests/test_ecommerce_comprehensive.py +852 -0
- pgoutput_decoder-0.1.0/tests/test_json_serialization.py +550 -0
- pgoutput_decoder-0.1.0/tests/test_types.py +64 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# GitHub Actions
|
|
4
|
+
- package-ecosystem: "github-actions"
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: "weekly"
|
|
8
|
+
labels:
|
|
9
|
+
- "dependencies"
|
|
10
|
+
- "github-actions"
|
|
11
|
+
|
|
12
|
+
# Cargo dependencies
|
|
13
|
+
- package-ecosystem: "cargo"
|
|
14
|
+
directory: "/"
|
|
15
|
+
schedule:
|
|
16
|
+
interval: "weekly"
|
|
17
|
+
labels:
|
|
18
|
+
- "dependencies"
|
|
19
|
+
- "rust"
|
|
20
|
+
|
|
21
|
+
# Python dependencies (pip)
|
|
22
|
+
- package-ecosystem: "pip"
|
|
23
|
+
directory: "/"
|
|
24
|
+
schedule:
|
|
25
|
+
interval: "weekly"
|
|
26
|
+
labels:
|
|
27
|
+
- "dependencies"
|
|
28
|
+
- "python"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Build
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, develop ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
name: Build - Python ${{ matrix.python-version }} on ${{ matrix.os }}
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
|
+
python-version: ['3.12', '3.13']
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v4
|
|
28
|
+
with:
|
|
29
|
+
version: "latest"
|
|
30
|
+
|
|
31
|
+
- name: Set up Rust
|
|
32
|
+
uses: dtolnay/rust-toolchain@stable
|
|
33
|
+
|
|
34
|
+
- name: Cache Rust dependencies
|
|
35
|
+
uses: Swatinem/rust-cache@v2
|
|
36
|
+
|
|
37
|
+
- name: Build wheels
|
|
38
|
+
uses: PyO3/maturin-action@v1
|
|
39
|
+
with:
|
|
40
|
+
args: --release --out dist --interpreter python${{ matrix.python-version }}
|
|
41
|
+
sccache: 'true'
|
|
42
|
+
manylinux: auto
|
|
43
|
+
|
|
44
|
+
- name: Upload wheels
|
|
45
|
+
uses: actions/upload-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: wheels-${{ matrix.os }}-py${{ matrix.python-version }}
|
|
48
|
+
path: dist
|
|
49
|
+
retention-days: 7
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, develop ]
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
CARGO_TERM_COLOR: always
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: Lint
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: '3.12'
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v4
|
|
26
|
+
with:
|
|
27
|
+
version: "latest"
|
|
28
|
+
|
|
29
|
+
- name: Set up Rust
|
|
30
|
+
uses: dtolnay/rust-toolchain@stable
|
|
31
|
+
with:
|
|
32
|
+
components: rustfmt, clippy
|
|
33
|
+
|
|
34
|
+
- name: Cache Rust dependencies
|
|
35
|
+
uses: Swatinem/rust-cache@v2
|
|
36
|
+
|
|
37
|
+
- name: Install maturin
|
|
38
|
+
run: uv tool install maturin
|
|
39
|
+
|
|
40
|
+
- name: Rust format check
|
|
41
|
+
run: cargo fmt --all -- --check
|
|
42
|
+
|
|
43
|
+
- name: Rust clippy
|
|
44
|
+
run: cargo clippy --all-targets --all-features -- -D warnings
|
|
45
|
+
|
|
46
|
+
- name: Python format check (ruff)
|
|
47
|
+
run: |
|
|
48
|
+
uv sync
|
|
49
|
+
uv run ruff check .
|
|
50
|
+
uv run ruff format --check .
|
|
51
|
+
|
|
52
|
+
test:
|
|
53
|
+
name: Test - Python ${{ matrix.python-version }} on ${{ matrix.os }}
|
|
54
|
+
runs-on: ${{ matrix.os }}
|
|
55
|
+
strategy:
|
|
56
|
+
fail-fast: false
|
|
57
|
+
matrix:
|
|
58
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
59
|
+
python-version: ['3.12', '3.13']
|
|
60
|
+
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
|
|
64
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
65
|
+
uses: actions/setup-python@v5
|
|
66
|
+
with:
|
|
67
|
+
python-version: ${{ matrix.python-version }}
|
|
68
|
+
|
|
69
|
+
- name: Install uv
|
|
70
|
+
uses: astral-sh/setup-uv@v4
|
|
71
|
+
with:
|
|
72
|
+
version: "latest"
|
|
73
|
+
|
|
74
|
+
- name: Set up Rust
|
|
75
|
+
uses: dtolnay/rust-toolchain@stable
|
|
76
|
+
|
|
77
|
+
- name: Cache Rust dependencies
|
|
78
|
+
uses: Swatinem/rust-cache@v2
|
|
79
|
+
|
|
80
|
+
- name: Set up Docker (for Testcontainers)
|
|
81
|
+
if: runner.os == 'Linux'
|
|
82
|
+
uses: docker/setup-buildx-action@v3
|
|
83
|
+
|
|
84
|
+
- name: Install maturin
|
|
85
|
+
run: uv tool install maturin
|
|
86
|
+
|
|
87
|
+
- name: Install dependencies and build
|
|
88
|
+
run: |
|
|
89
|
+
uv sync
|
|
90
|
+
uv run maturin develop
|
|
91
|
+
|
|
92
|
+
- name: Run tests (Linux - with Docker)
|
|
93
|
+
if: runner.os == 'Linux'
|
|
94
|
+
run: uv run pytest tests/ -v --tb=short
|
|
95
|
+
|
|
96
|
+
- name: Run tests (macOS/Windows - skip Docker tests)
|
|
97
|
+
if: runner.os != 'Linux'
|
|
98
|
+
run: uv run pytest tests/ -v --tb=short -m "not docker"
|
|
99
|
+
|
|
100
|
+
coverage:
|
|
101
|
+
name: Code Coverage
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
|
|
104
|
+
steps:
|
|
105
|
+
- uses: actions/checkout@v4
|
|
106
|
+
|
|
107
|
+
- name: Set up Python
|
|
108
|
+
uses: actions/setup-python@v5
|
|
109
|
+
with:
|
|
110
|
+
python-version: '3.12'
|
|
111
|
+
|
|
112
|
+
- name: Install uv
|
|
113
|
+
uses: astral-sh/setup-uv@v4
|
|
114
|
+
with:
|
|
115
|
+
version: "latest"
|
|
116
|
+
|
|
117
|
+
- name: Set up Rust
|
|
118
|
+
uses: dtolnay/rust-toolchain@stable
|
|
119
|
+
with:
|
|
120
|
+
components: llvm-tools-preview
|
|
121
|
+
|
|
122
|
+
- name: Cache Rust dependencies
|
|
123
|
+
uses: Swatinem/rust-cache@v2
|
|
124
|
+
|
|
125
|
+
- name: Set up Docker
|
|
126
|
+
uses: docker/setup-buildx-action@v3
|
|
127
|
+
|
|
128
|
+
- name: Install maturin
|
|
129
|
+
run: uv tool install maturin
|
|
130
|
+
|
|
131
|
+
- name: Install cargo-llvm-cov
|
|
132
|
+
uses: taiki-e/install-action@cargo-llvm-cov
|
|
133
|
+
|
|
134
|
+
- name: Install dependencies and build
|
|
135
|
+
run: |
|
|
136
|
+
uv sync
|
|
137
|
+
rm -f *.profraw # Clean old coverage data
|
|
138
|
+
|
|
139
|
+
- name: Build with maturin (instrumented)
|
|
140
|
+
run: uv tool run maturin develop
|
|
141
|
+
env:
|
|
142
|
+
RUSTFLAGS: '-C instrument-coverage'
|
|
143
|
+
LLVM_PROFILE_FILE: 'coverage-%p-%m.profraw'
|
|
144
|
+
|
|
145
|
+
- name: Run Python tests with coverage
|
|
146
|
+
run: |
|
|
147
|
+
uv run pytest tests/ --cov=pgoutput_decoder --cov-report=xml:python-coverage.xml --cov-report=term
|
|
148
|
+
env:
|
|
149
|
+
LLVM_PROFILE_FILE: 'coverage-%p-%m.profraw'
|
|
150
|
+
|
|
151
|
+
- name: Generate Rust coverage report
|
|
152
|
+
run: |
|
|
153
|
+
# Check if profraw files exist
|
|
154
|
+
ls -la *.profraw || echo "Warning: No profraw files found in workspace"
|
|
155
|
+
# Find llvm tools from rustc sysroot
|
|
156
|
+
SYSROOT=$(rustc --print sysroot)
|
|
157
|
+
LLVM_PROFDATA=$(find "$SYSROOT" -name llvm-profdata | head -1)
|
|
158
|
+
LLVM_COV=$(find "$SYSROOT" -name llvm-cov | head -1)
|
|
159
|
+
echo "Using llvm-profdata: $LLVM_PROFDATA"
|
|
160
|
+
echo "Using llvm-cov: $LLVM_COV"
|
|
161
|
+
# Merge profraw files
|
|
162
|
+
"$LLVM_PROFDATA" merge -sparse *.profraw -o coverage.profdata
|
|
163
|
+
# Find the compiled Python extension in target/debug (where maturin builds it)
|
|
164
|
+
# Exclude deps/ directory which contains Rust build dependencies
|
|
165
|
+
echo "Searching for Python extension in target/debug..."
|
|
166
|
+
EXTENSION=$(find target/debug -maxdepth 1 -type f \( -name "*.so" -o -name "*.dylib" \) 2>/dev/null | head -1)
|
|
167
|
+
echo "Using extension: $EXTENSION"
|
|
168
|
+
if [ -z "$EXTENSION" ] || [ ! -f "$EXTENSION" ]; then
|
|
169
|
+
echo "Error: Could not find Python extension in target/debug"
|
|
170
|
+
echo "Contents of target/debug:"
|
|
171
|
+
ls -la target/debug/ 2>/dev/null || echo "target/debug not found"
|
|
172
|
+
echo "Searching for all .so/.dylib files:"
|
|
173
|
+
find target -name "*.so" -o -name "*.dylib" 2>/dev/null || echo "No shared libraries found"
|
|
174
|
+
exit 1
|
|
175
|
+
fi
|
|
176
|
+
# Generate lcov report
|
|
177
|
+
"$LLVM_COV" export --format=lcov \
|
|
178
|
+
--instr-profile=coverage.profdata \
|
|
179
|
+
--ignore-filename-regex='/.cargo/' \
|
|
180
|
+
--ignore-filename-regex='/rustc/' \
|
|
181
|
+
"$EXTENSION" > rust-coverage.lcov
|
|
182
|
+
# Show summary
|
|
183
|
+
"$LLVM_COV" report \
|
|
184
|
+
--instr-profile=coverage.profdata \
|
|
185
|
+
--ignore-filename-regex='/.cargo/' \
|
|
186
|
+
--ignore-filename-regex='/rustc/' \
|
|
187
|
+
"$EXTENSION"
|
|
188
|
+
|
|
189
|
+
- name: Upload Python coverage to Codecov
|
|
190
|
+
uses: codecov/codecov-action@v5
|
|
191
|
+
with:
|
|
192
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
193
|
+
files: ./python-coverage.xml
|
|
194
|
+
flags: python
|
|
195
|
+
name: python-coverage
|
|
196
|
+
fail_ci_if_error: false
|
|
197
|
+
|
|
198
|
+
- name: Upload Rust coverage to Codecov
|
|
199
|
+
uses: codecov/codecov-action@v5
|
|
200
|
+
with:
|
|
201
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
202
|
+
files: ./rust-coverage.lcov
|
|
203
|
+
flags: rust
|
|
204
|
+
name: rust-coverage
|
|
205
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*.*.*'
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build-wheels:
|
|
14
|
+
name: Build wheels on ${{ matrix.os }}
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: '3.12'
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v4
|
|
30
|
+
with:
|
|
31
|
+
version: "latest"
|
|
32
|
+
|
|
33
|
+
- name: Build wheels
|
|
34
|
+
uses: PyO3/maturin-action@v1
|
|
35
|
+
with:
|
|
36
|
+
args: --release --out dist --interpreter 3.12 3.13
|
|
37
|
+
sccache: 'true'
|
|
38
|
+
manylinux: auto
|
|
39
|
+
|
|
40
|
+
- name: Upload wheels
|
|
41
|
+
uses: actions/upload-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: wheels-${{ matrix.os }}
|
|
44
|
+
path: dist
|
|
45
|
+
|
|
46
|
+
build-sdist:
|
|
47
|
+
name: Build source distribution
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
|
|
52
|
+
- name: Set up Python
|
|
53
|
+
uses: actions/setup-python@v5
|
|
54
|
+
with:
|
|
55
|
+
python-version: '3.12'
|
|
56
|
+
|
|
57
|
+
- name: Install uv
|
|
58
|
+
uses: astral-sh/setup-uv@v4
|
|
59
|
+
with:
|
|
60
|
+
version: "latest"
|
|
61
|
+
|
|
62
|
+
- name: Build sdist
|
|
63
|
+
uses: PyO3/maturin-action@v1
|
|
64
|
+
with:
|
|
65
|
+
command: sdist
|
|
66
|
+
args: --out dist
|
|
67
|
+
|
|
68
|
+
- name: Upload sdist
|
|
69
|
+
uses: actions/upload-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: sdist
|
|
72
|
+
path: dist
|
|
73
|
+
|
|
74
|
+
publish-test-pypi:
|
|
75
|
+
name: Publish to Test PyPI
|
|
76
|
+
needs: [build-wheels, build-sdist]
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
if: github.event_name == 'workflow_dispatch' || contains(github.ref, 'rc') || contains(github.ref, 'beta')
|
|
79
|
+
|
|
80
|
+
environment:
|
|
81
|
+
name: testpypi
|
|
82
|
+
url: https://test.pypi.org/p/pgoutput-decoder
|
|
83
|
+
|
|
84
|
+
permissions:
|
|
85
|
+
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
86
|
+
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/download-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
pattern: wheels-*
|
|
91
|
+
path: dist
|
|
92
|
+
merge-multiple: true
|
|
93
|
+
|
|
94
|
+
- uses: actions/download-artifact@v4
|
|
95
|
+
with:
|
|
96
|
+
name: sdist
|
|
97
|
+
path: dist
|
|
98
|
+
|
|
99
|
+
- name: Publish to Test PyPI
|
|
100
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
101
|
+
with:
|
|
102
|
+
repository-url: https://test.pypi.org/legacy/
|
|
103
|
+
skip-existing: true
|
|
104
|
+
|
|
105
|
+
publish-pypi:
|
|
106
|
+
name: Publish to PyPI
|
|
107
|
+
needs: [build-wheels, build-sdist]
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'rc') && !contains(github.ref, 'beta')
|
|
110
|
+
|
|
111
|
+
environment:
|
|
112
|
+
name: pypi
|
|
113
|
+
url: https://pypi.org/p/pgoutput-decoder
|
|
114
|
+
|
|
115
|
+
permissions:
|
|
116
|
+
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
117
|
+
|
|
118
|
+
steps:
|
|
119
|
+
- uses: actions/download-artifact@v4
|
|
120
|
+
with:
|
|
121
|
+
pattern: wheels-*
|
|
122
|
+
path: dist
|
|
123
|
+
merge-multiple: true
|
|
124
|
+
|
|
125
|
+
- uses: actions/download-artifact@v4
|
|
126
|
+
with:
|
|
127
|
+
name: sdist
|
|
128
|
+
path: dist
|
|
129
|
+
|
|
130
|
+
- name: Publish to PyPI
|
|
131
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
132
|
+
with:
|
|
133
|
+
skip-existing: true
|
|
134
|
+
|
|
135
|
+
create-github-release:
|
|
136
|
+
name: Create GitHub Release
|
|
137
|
+
needs: [publish-pypi]
|
|
138
|
+
runs-on: ubuntu-latest
|
|
139
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
140
|
+
|
|
141
|
+
steps:
|
|
142
|
+
- uses: actions/checkout@v4
|
|
143
|
+
|
|
144
|
+
- uses: actions/download-artifact@v4
|
|
145
|
+
with:
|
|
146
|
+
pattern: wheels-*
|
|
147
|
+
path: dist
|
|
148
|
+
merge-multiple: true
|
|
149
|
+
|
|
150
|
+
- uses: actions/download-artifact@v4
|
|
151
|
+
with:
|
|
152
|
+
name: sdist
|
|
153
|
+
path: dist
|
|
154
|
+
|
|
155
|
+
- name: Create Release
|
|
156
|
+
uses: softprops/action-gh-release@v1
|
|
157
|
+
with:
|
|
158
|
+
files: dist/*
|
|
159
|
+
generate_release_notes: true
|
|
160
|
+
draft: false
|
|
161
|
+
prerelease: false
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Coverage reports
|
|
13
|
+
htmlcov/
|
|
14
|
+
.coverage
|
|
15
|
+
coverage.xml
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
|
|
18
|
+
# Rust-generated files
|
|
19
|
+
target/
|
|
20
|
+
Cargo.lock
|
|
21
|
+
*.so
|
|
22
|
+
*.pyd
|
|
23
|
+
*.dylib
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.idea/
|
|
27
|
+
.vscode/
|
|
28
|
+
*.swp
|
|
29
|
+
*.swo
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
# Rust formatting and linting
|
|
3
|
+
- repo: local
|
|
4
|
+
hooks:
|
|
5
|
+
- id: cargo-fmt
|
|
6
|
+
name: cargo fmt
|
|
7
|
+
entry: cargo fmt
|
|
8
|
+
language: system
|
|
9
|
+
types: [rust]
|
|
10
|
+
pass_filenames: false
|
|
11
|
+
|
|
12
|
+
- id: cargo-clippy
|
|
13
|
+
name: cargo clippy
|
|
14
|
+
entry: cargo clippy --all-targets --all-features -- -D warnings
|
|
15
|
+
language: system
|
|
16
|
+
types: [rust]
|
|
17
|
+
pass_filenames: false
|
|
18
|
+
|
|
19
|
+
# Python formatting and linting via ruff
|
|
20
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
21
|
+
rev: v0.8.4
|
|
22
|
+
hooks:
|
|
23
|
+
- id: ruff
|
|
24
|
+
args: [--fix]
|
|
25
|
+
- id: ruff-format
|
|
26
|
+
|
|
27
|
+
# General file checks
|
|
28
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
29
|
+
rev: v5.0.0
|
|
30
|
+
hooks:
|
|
31
|
+
- id: trailing-whitespace
|
|
32
|
+
- id: end-of-file-fixer
|
|
33
|
+
- id: check-yaml
|
|
34
|
+
- id: check-toml
|
|
35
|
+
- id: check-merge-conflict
|
|
36
|
+
- id: check-added-large-files
|
|
37
|
+
args: ['--maxkb=1000']
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
🤖 Project Persona
|
|
2
|
+
You are an expert full-stack developer specializing in high-performance Python/Rust systems. You prioritize memory safety, type correctness, and efficient dependency management.
|
|
3
|
+
|
|
4
|
+
🛠 Tech Stack
|
|
5
|
+
Package Manager: uv (use uv run, uv sync, uv add)
|
|
6
|
+
|
|
7
|
+
Linter/Formatter: ruff
|
|
8
|
+
|
|
9
|
+
Backend: Rust (using PyO3 for bindings)
|
|
10
|
+
|
|
11
|
+
Build System: maturin
|
|
12
|
+
|
|
13
|
+
Testing: pytest + Testcontainers (for E2E)
|
|
14
|
+
|
|
15
|
+
📂 Project Structure
|
|
16
|
+
Plaintext
|
|
17
|
+
.
|
|
18
|
+
├── Cargo.toml # Rust metadata & dependencies
|
|
19
|
+
├── pyproject.toml # Python metadata, Ruff config, Maturin settings
|
|
20
|
+
├── justfile # Task runner for common commands
|
|
21
|
+
├── .pre-commit-config.yaml # Pre-commit hooks configuration
|
|
22
|
+
├── src/ # Rust source code
|
|
23
|
+
│ └── lib.rs # PyO3 module definitions
|
|
24
|
+
├── python/ # Python source code
|
|
25
|
+
│ └── my_project/ # Main Python package
|
|
26
|
+
│ ├── __init__.py
|
|
27
|
+
│ └── core.py
|
|
28
|
+
├── tests/ # E2E and Unit tests
|
|
29
|
+
│ └── e2e/ # Testcontainers-based tests
|
|
30
|
+
├── .vscode/settings.json # VS Code configuration
|
|
31
|
+
└── .python-version # Managed by uv
|
|
32
|
+
🚀 Development Workflow
|
|
33
|
+
1. Setup & Installation
|
|
34
|
+
Always use uv for environment management.
|
|
35
|
+
|
|
36
|
+
Sync environment: uv sync
|
|
37
|
+
|
|
38
|
+
Build Rust bindings (dev): uv run maturin develop (This installs the Rust module into the current venv).
|
|
39
|
+
|
|
40
|
+
Quick setup: just setup (requires just: brew install just)
|
|
41
|
+
|
|
42
|
+
2. Linting & Formatting
|
|
43
|
+
We use ruff for everything Python and clippy for Rust.
|
|
44
|
+
|
|
45
|
+
**Recommended**: Use pre-commit hooks to catch issues before commit:
|
|
46
|
+
|
|
47
|
+
bash
|
|
48
|
+
# One-time setup
|
|
49
|
+
pip install pre-commit
|
|
50
|
+
pre-commit install
|
|
51
|
+
|
|
52
|
+
# Manual run
|
|
53
|
+
pre-commit run --all-files
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
**Manual checks:**
|
|
57
|
+
|
|
58
|
+
Python:
|
|
59
|
+
- Check: uv run ruff check .
|
|
60
|
+
- Format: uv run ruff format .
|
|
61
|
+
|
|
62
|
+
Rust:
|
|
63
|
+
- Format: cargo fmt
|
|
64
|
+
- Lint: cargo clippy --all-targets --all-features -- -D warnings
|
|
65
|
+
|
|
66
|
+
**Quick commands** (requires just):
|
|
67
|
+
- just check - Run all checks
|
|
68
|
+
- just lint - Run all linters
|
|
69
|
+
- just fmt - Format all code
|
|
70
|
+
- just pre-commit - Full pre-commit check
|
|
71
|
+
|
|
72
|
+
**Auto-watch mode** (requires cargo-watch):
|
|
73
|
+
bash
|
|
74
|
+
just watch # Auto-run clippy on file changes
|
|
75
|
+
|
|
76
|
+
Always run just check or pre-commit before pushing to avoid CI failures.
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
3. Testing
|
|
80
|
+
Run all tests: uv run pytest
|
|
81
|
+
|
|
82
|
+
E2E Tests: Ensure Docker is running, as these use Testcontainers.
|
|
83
|
+
|
|
84
|
+
Note: Use the testcontainers[postgres] (or relevant module) in pyproject.toml.
|
|
85
|
+
|
|
86
|
+
Quick: just test (runs both Rust and Python tests)
|
|
87
|
+
|
|
88
|
+
📝 Coding Standards
|
|
89
|
+
Rust Bindings: Prefer #[pyfunction] and #[pymodule]. Avoid manual type conversions; let PyO3 handle the heavy lifting where possible.
|
|
90
|
+
|
|
91
|
+
Python Types: Strict type hinting is mandatory. All function signatures must have type hints.
|
|
92
|
+
|
|
93
|
+
Testing Pattern:
|
|
94
|
+
|
|
95
|
+
Use @pytest.fixture for Testcontainers setup.
|
|
96
|
+
|
|
97
|
+
Keep E2E tests in tests/e2e/ to separate them from fast unit tests.
|
|
98
|
+
|
|
99
|
+
Performance: If a Python loop is identified as a bottleneck, move the logic to the Rust src/ directory.
|
|
100
|
+
|
|
101
|
+
⚠️ Constraints
|
|
102
|
+
Never use pip or venv directly. Always go through uv.
|
|
103
|
+
|
|
104
|
+
Never commit the .venv directory or Rust target/ binaries.
|
|
105
|
+
|
|
106
|
+
Do not add heavy Python dependencies if the logic can be implemented efficiently in Rust.
|
|
107
|
+
|
|
108
|
+
💡 Example: End-to-End Test with Testcontainers
|
|
109
|
+
When writing E2E tests, follow this pattern:
|
|
110
|
+
|
|
111
|
+
Python
|
|
112
|
+
import pytest
|
|
113
|
+
from testcontainers.postgres import PostgresContainer
|
|
114
|
+
from my_project import rust_backend # Your PyO3 module
|
|
115
|
+
|
|
116
|
+
@pytest.fixture(scope="module")
|
|
117
|
+
def db_container():
|
|
118
|
+
with PostgresContainer("postgres:18.1-alpine") as postgres:
|
|
119
|
+
yield postgres
|
|
120
|
+
|
|
121
|
+
def test_rust_db_integration(db_container):
|
|
122
|
+
conn_str = db_container.get_connection_url()
|
|
123
|
+
# Logic calling your Rust bindings to interact with the container
|
|
124
|
+
result = rust_backend.process_data_in_db(conn_str)
|
|
125
|
+
assert result is True
|