csv-salvage 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.
Files changed (46) hide show
  1. csv_salvage-0.1.0/.gitattributes +25 -0
  2. csv_salvage-0.1.0/.github/dependabot.yml +44 -0
  3. csv_salvage-0.1.0/.github/workflows/ci.yml +275 -0
  4. csv_salvage-0.1.0/.github/workflows/dependabot-automerge.yml +28 -0
  5. csv_salvage-0.1.0/.github/workflows/publish.yml +314 -0
  6. csv_salvage-0.1.0/.gitignore +77 -0
  7. csv_salvage-0.1.0/.readthedocs.yaml +15 -0
  8. csv_salvage-0.1.0/AGENTS.md +135 -0
  9. csv_salvage-0.1.0/CHANGELOG.md +32 -0
  10. csv_salvage-0.1.0/CLAUDE.md +37 -0
  11. csv_salvage-0.1.0/CODE_OF_CONDUCT.md +74 -0
  12. csv_salvage-0.1.0/CONTRIBUTING.md +118 -0
  13. csv_salvage-0.1.0/Cargo.lock +132 -0
  14. csv_salvage-0.1.0/Cargo.toml +24 -0
  15. csv_salvage-0.1.0/LICENSE.md +13 -0
  16. csv_salvage-0.1.0/PKG-INFO +197 -0
  17. csv_salvage-0.1.0/README.md +170 -0
  18. csv_salvage-0.1.0/SECURITY.md +33 -0
  19. csv_salvage-0.1.0/codecov.yml +60 -0
  20. csv_salvage-0.1.0/docs/api.md +89 -0
  21. csv_salvage-0.1.0/docs/architecture.md +37 -0
  22. csv_salvage-0.1.0/docs/getting-started.md +78 -0
  23. csv_salvage-0.1.0/docs/index.md +37 -0
  24. csv_salvage-0.1.0/docs/roadmap-1.0.md +40 -0
  25. csv_salvage-0.1.0/docs/support.md +36 -0
  26. csv_salvage-0.1.0/mkdocs.yml +18 -0
  27. csv_salvage-0.1.0/pyproject.toml +57 -0
  28. csv_salvage-0.1.0/python/csv_salvage/__init__.py +35 -0
  29. csv_salvage-0.1.0/python/csv_salvage/_csv_salvage_backend.pyi +67 -0
  30. csv_salvage-0.1.0/python/csv_salvage/errors.py +18 -0
  31. csv_salvage-0.1.0/python/csv_salvage/py.typed +1 -0
  32. csv_salvage-0.1.0/python/csv_salvage/reader.py +84 -0
  33. csv_salvage-0.1.0/python/csv_salvage/salvage.py +120 -0
  34. csv_salvage-0.1.0/python/csv_salvage/validation.py +66 -0
  35. csv_salvage-0.1.0/src/error.rs +54 -0
  36. csv_salvage-0.1.0/src/heuristics.rs +127 -0
  37. csv_salvage-0.1.0/src/lib.rs +290 -0
  38. csv_salvage-0.1.0/src/parser.rs +368 -0
  39. csv_salvage-0.1.0/src/salvage.rs +253 -0
  40. csv_salvage-0.1.0/tests/test_release.py +59 -0
  41. csv_salvage-0.1.0/tests/test_salvage.py +114 -0
  42. csv_salvage-0.1.0/tests/test_validation.py +110 -0
  43. csv_salvage-0.1.0/tools/benchmark.py +83 -0
  44. csv_salvage-0.1.0/tools/validate_distribution.py +104 -0
  45. csv_salvage-0.1.0/tools/validate_release.py +37 -0
  46. csv_salvage-0.1.0/uv.lock +1581 -0
@@ -0,0 +1,25 @@
1
+ # Normalize text files to LF in the repository while allowing Git to detect
2
+ # the working-tree convention on each platform.
3
+ * text=auto
4
+
5
+ # Source and configuration files.
6
+ *.py text diff=python
7
+ *.pyi text diff=python
8
+ *.rs text diff=rust
9
+ *.toml text diff=toml
10
+ *.md text
11
+ *.ps1 text
12
+ *.yml text
13
+ *.yaml text
14
+ *.xml text
15
+ Cargo.lock text
16
+ uv.lock text
17
+
18
+ # Common generated or binary artifacts.
19
+ *.dll binary
20
+ *.pdb binary
21
+ *.whl binary
22
+ *.png binary
23
+ *.jpg binary
24
+ *.jpeg binary
25
+ *.gif binary
@@ -0,0 +1,44 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ day: "monday"
8
+ time: "06:00"
9
+ timezone: "America/Santiago"
10
+ groups:
11
+ github-actions:
12
+ patterns:
13
+ - "*"
14
+ open-pull-requests-limit: 5
15
+
16
+ - package-ecosystem: "cargo"
17
+ directory: "/"
18
+ schedule:
19
+ interval: "weekly"
20
+ day: "monday"
21
+ time: "06:00"
22
+ timezone: "America/Santiago"
23
+ groups:
24
+ rust-dependencies:
25
+ patterns:
26
+ - "*"
27
+ open-pull-requests-limit: 5
28
+ commit-message:
29
+ prefix: "deps"
30
+
31
+ - package-ecosystem: "pip"
32
+ directory: "/"
33
+ schedule:
34
+ interval: "weekly"
35
+ day: "monday"
36
+ time: "06:00"
37
+ timezone: "America/Santiago"
38
+ groups:
39
+ python-dependencies:
40
+ patterns:
41
+ - "*"
42
+ open-pull-requests-limit: 5
43
+ commit-message:
44
+ prefix: "deps"
@@ -0,0 +1,275 @@
1
+ # Based on maturin output; preserve custom validation and wheel tests when updating.
2
+ name: CI
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - main
8
+ - master
9
+ tags:
10
+ - "v*"
11
+ pull_request:
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ tests:
19
+ runs-on: ${{ matrix.runner }}
20
+ strategy:
21
+ matrix:
22
+ runner: [ubuntu-latest, macos-latest, windows-latest]
23
+ python: ["3.9", "3.14"]
24
+ steps:
25
+ - uses: actions/checkout@v6
26
+ - uses: actions/setup-python@v6
27
+ with:
28
+ python-version: ${{ matrix.python }}
29
+ - uses: astral-sh/setup-uv@v7
30
+ with:
31
+ python-version: ${{ matrix.python }}
32
+ - run: uv sync --locked
33
+ - run: cargo fmt --check
34
+ - run: cargo clippy --all-targets --all-features -- -D warnings
35
+ - name: Run Cargo tests
36
+ run: cargo test --verbose --no-default-features
37
+ env:
38
+ RUSTFLAGS: ${{ runner.os == 'macOS' && '-C link-arg=-undefined -C link-arg=dynamic_lookup' || '' }}
39
+ - run: uv run python -m unittest discover -s tests -v
40
+
41
+ msrv:
42
+ runs-on: windows-latest
43
+ steps:
44
+ - uses: actions/checkout@v6
45
+ - uses: actions/setup-python@v6
46
+ with:
47
+ python-version: "3.13"
48
+ - name: Check minimum Rust version
49
+ run: |
50
+ rustup toolchain install 1.85.0 --profile minimal
51
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
52
+ $env:PYO3_PYTHON = python -c "import sys; print(sys.executable)"
53
+ cargo +1.85.0 check --locked
54
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
55
+
56
+ docs:
57
+ runs-on: ubuntu-latest
58
+ steps:
59
+ - uses: actions/checkout@v6
60
+ - uses: astral-sh/setup-uv@v7
61
+ - run: uv sync --locked --group docs
62
+ - run: uv run --group docs mkdocs build --strict
63
+
64
+ linux:
65
+ runs-on: ${{ matrix.platform.runner }}
66
+ strategy:
67
+ matrix:
68
+ platform:
69
+ - runner: ubuntu-22.04
70
+ target: x86_64
71
+ - runner: ubuntu-22.04
72
+ target: x86
73
+ - runner: ubuntu-22.04
74
+ target: aarch64
75
+ - runner: ubuntu-22.04
76
+ target: armv7
77
+ - runner: ubuntu-22.04
78
+ target: s390x
79
+ - runner: ubuntu-22.04
80
+ target: ppc64le
81
+ steps:
82
+ - uses: actions/checkout@v6
83
+ - uses: actions/setup-python@v6
84
+ with:
85
+ python-version: "3.14"
86
+ - name: Build wheels
87
+ uses: PyO3/maturin-action@v1
88
+ with:
89
+ target: ${{ matrix.platform.target }}
90
+ args: --release --out dist --find-interpreter
91
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
92
+ manylinux: auto
93
+ - name: Upload wheels
94
+ uses: actions/upload-artifact@v6
95
+ with:
96
+ name: wheels-linux-${{ matrix.platform.target }}
97
+ path: dist
98
+
99
+ musllinux:
100
+ runs-on: ${{ matrix.platform.runner }}
101
+ strategy:
102
+ matrix:
103
+ platform:
104
+ - runner: ubuntu-22.04
105
+ target: x86_64
106
+ - runner: ubuntu-22.04
107
+ target: x86
108
+ - runner: ubuntu-22.04
109
+ target: aarch64
110
+ - runner: ubuntu-22.04
111
+ target: armv7
112
+ steps:
113
+ - uses: actions/checkout@v6
114
+ - uses: actions/setup-python@v6
115
+ with:
116
+ python-version: "3.14"
117
+ - name: Build wheels
118
+ uses: PyO3/maturin-action@v1
119
+ with:
120
+ target: ${{ matrix.platform.target }}
121
+ args: --release --out dist --find-interpreter
122
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
123
+ manylinux: musllinux_1_2
124
+ - name: Upload wheels
125
+ uses: actions/upload-artifact@v6
126
+ with:
127
+ name: wheels-musllinux-${{ matrix.platform.target }}
128
+ path: dist
129
+
130
+ windows:
131
+ runs-on: ${{ matrix.runner }}
132
+ strategy:
133
+ matrix:
134
+ include:
135
+ - runner: windows-latest
136
+ target: x64
137
+ rust_target: x86_64-pc-windows-msvc
138
+ python_arch: x64
139
+ python: "3.9"
140
+ - runner: windows-latest
141
+ target: x64
142
+ rust_target: x86_64-pc-windows-msvc
143
+ python_arch: x64
144
+ python: "3.10"
145
+ - runner: windows-latest
146
+ target: x64
147
+ rust_target: x86_64-pc-windows-msvc
148
+ python_arch: x64
149
+ python: "3.11"
150
+ - runner: windows-latest
151
+ target: x64
152
+ rust_target: x86_64-pc-windows-msvc
153
+ python_arch: x64
154
+ python: "3.12"
155
+ - runner: windows-latest
156
+ target: x64
157
+ rust_target: x86_64-pc-windows-msvc
158
+ python_arch: x64
159
+ python: "3.13"
160
+ - runner: windows-latest
161
+ target: x64
162
+ rust_target: x86_64-pc-windows-msvc
163
+ python_arch: x64
164
+ python: "3.14"
165
+ - runner: windows-2022
166
+ target: x64
167
+ rust_target: x86_64-pc-windows-msvc
168
+ python_arch: x64
169
+ python: "3.13"
170
+ - runner: windows-latest
171
+ target: x86
172
+ rust_target: i686-pc-windows-msvc
173
+ python_arch: x86
174
+ python: "3.13"
175
+ - runner: windows-11-arm
176
+ target: aarch64
177
+ rust_target: aarch64-pc-windows-msvc
178
+ python_arch: arm64
179
+ python: "3.13"
180
+ steps:
181
+ - uses: actions/checkout@v6
182
+ - uses: actions/setup-python@v6
183
+ with:
184
+ python-version: ${{ matrix.python }}
185
+ architecture: ${{ matrix.python_arch }}
186
+ - name: Build wheels
187
+ uses: PyO3/maturin-action@v1
188
+ with:
189
+ target: ${{ matrix.target }}
190
+ args: --release --locked --out dist --interpreter python
191
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
192
+ - name: Test native crate
193
+ run: |
194
+ $env:PYO3_PYTHON = python -c "import sys; print(sys.executable)"
195
+ cargo test --locked --target ${{ matrix.rust_target }} --no-default-features
196
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
197
+ - name: Validate wheel contents
198
+ run: python tools/validate_distribution.py dist
199
+ - name: Test installed Windows wheel
200
+ run: |
201
+ python -m pip install "typing-extensions>=4.0"
202
+ python -m pip install --force-reinstall --no-deps --no-index --find-links dist csv_salvage
203
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
204
+ python -m unittest discover -s tests -v
205
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
206
+ - name: Upload wheels
207
+ if: matrix.runner != 'windows-2022'
208
+ uses: actions/upload-artifact@v6
209
+ with:
210
+ name: wheels-windows-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.python }}
211
+ path: dist
212
+
213
+ macos:
214
+ runs-on: ${{ matrix.platform.runner }}
215
+ strategy:
216
+ matrix:
217
+ platform:
218
+ - runner: macos-15-intel
219
+ target: x86_64
220
+ - runner: macos-latest
221
+ target: aarch64
222
+ steps:
223
+ - uses: actions/checkout@v6
224
+ - uses: actions/setup-python@v6
225
+ with:
226
+ python-version: "3.14"
227
+ - name: Build wheels
228
+ uses: PyO3/maturin-action@v1
229
+ with:
230
+ target: ${{ matrix.platform.target }}
231
+ args: --release --out dist --find-interpreter
232
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
233
+ - name: Upload wheels
234
+ uses: actions/upload-artifact@v6
235
+ with:
236
+ name: wheels-macos-${{ matrix.platform.target }}
237
+ path: dist
238
+
239
+ sdist:
240
+ runs-on: windows-latest
241
+ steps:
242
+ - uses: actions/checkout@v6
243
+ - uses: actions/setup-python@v6
244
+ with:
245
+ python-version: "3.13"
246
+ - uses: astral-sh/setup-uv@v7
247
+ - uses: PyO3/maturin-action@v1
248
+ with:
249
+ command: sdist
250
+ args: --out dist
251
+ - name: Check distribution metadata
252
+ run: |
253
+ uv sync --locked --group release
254
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
255
+ uv run --group release python -m twine check --strict dist/*
256
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
257
+ python tools/validate_distribution.py dist
258
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
259
+ - name: Rebuild and test source distribution
260
+ run: |
261
+ $source = @(Get-ChildItem dist/*.tar.gz)
262
+ if ($source.Count -ne 1) { throw "Expected one source distribution" }
263
+ python -m pip wheel --no-deps --wheel-dir rebuilt $source[0].FullName
264
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
265
+ python tools/validate_distribution.py rebuilt
266
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
267
+ python -m pip install "typing-extensions>=4.0"
268
+ python -m pip install --force-reinstall --no-deps --no-index --find-links rebuilt csv_salvage
269
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
270
+ python -m unittest discover -s tests -v
271
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
272
+ - uses: actions/upload-artifact@v6
273
+ with:
274
+ name: wheels-sdist
275
+ path: dist
@@ -0,0 +1,28 @@
1
+ name: Dependabot Auto-merge
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ permissions:
7
+ contents: write
8
+ pull-requests: write
9
+
10
+ jobs:
11
+ dependabot-automerge:
12
+ runs-on: ubuntu-latest
13
+ if: github.actor == 'dependabot[bot]'
14
+ steps:
15
+ - name: Obtener metadatos de Dependabot
16
+ id: metadata
17
+ uses: dependabot/fetch-metadata@v2
18
+ with:
19
+ github-token: "${{ secrets.GITHUB_TOKEN }}"
20
+
21
+ - name: Aprobar PR y habilitar auto-merge
22
+ if: steps.metadata.outputs.update-type != 'version-update:semver-major'
23
+ run: |
24
+ gh pr review --approve "$PR_URL"
25
+ gh pr merge --auto --squash "$PR_URL"
26
+ env:
27
+ PR_URL: ${{ github.event.pull_request.html_url }}
28
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,314 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ release:
8
+ types: [published]
9
+ workflow_dispatch:
10
+ inputs:
11
+ tag:
12
+ description: "Release tag to build and publish, for example v0.1.0"
13
+ required: false
14
+ type: string
15
+ publish:
16
+ description: "Publish to PyPI after validation (disable for a rehearsal)"
17
+ required: false
18
+ type: boolean
19
+ default: true
20
+
21
+ # Serialize manual, tag-triggered, and release-triggered publication of the same version.
22
+ concurrency:
23
+ group: release-${{ github.event.release.tag_name || inputs.tag || github.ref_name }}
24
+ cancel-in-progress: false
25
+
26
+ permissions:
27
+ contents: read
28
+
29
+ jobs:
30
+ validate:
31
+ runs-on: ubuntu-latest
32
+ outputs:
33
+ sha: ${{ steps.source.outputs.sha }}
34
+ modern_release: ${{ steps.capabilities.outputs.modern_release }}
35
+ steps:
36
+ - uses: actions/checkout@v6
37
+ with:
38
+ ref: ${{ inputs.tag || github.ref }}
39
+ - uses: actions/setup-python@v6
40
+ with:
41
+ python-version: "3.13"
42
+ - name: Detect release tooling
43
+ id: capabilities
44
+ run: |
45
+ if [[ -f tools/validate_release.py && -f tools/validate_distribution.py && -d docs ]]; then
46
+ echo "modern_release=true" >> "$GITHUB_OUTPUT"
47
+ else
48
+ echo "modern_release=false" >> "$GITHUB_OUTPUT"
49
+ fi
50
+ - name: Validate modern release
51
+ if: steps.capabilities.outputs.modern_release == 'true'
52
+ env:
53
+ RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag || github.ref_name }}
54
+ RELEASE_PRERELEASE: ${{ github.event_name == 'release' && toJSON(github.event.release.prerelease) || '' }}
55
+ run: python tools/validate_release.py
56
+ - name: Resolve immutable build commit
57
+ id: source
58
+ run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
59
+ - uses: astral-sh/setup-uv@v7
60
+ - run: uv sync --locked
61
+ - run: uv run python -m unittest discover -s tests -v
62
+ - if: steps.capabilities.outputs.modern_release == 'true'
63
+ run: uv run --group docs mkdocs build --strict
64
+
65
+ linux:
66
+ needs: validate
67
+ runs-on: ${{ matrix.platform.runner }}
68
+ strategy:
69
+ matrix:
70
+ platform:
71
+ - runner: ubuntu-22.04
72
+ target: x86_64
73
+ - runner: ubuntu-22.04
74
+ target: x86
75
+ - runner: ubuntu-22.04
76
+ target: aarch64
77
+ - runner: ubuntu-22.04
78
+ target: armv7
79
+ - runner: ubuntu-22.04
80
+ target: s390x
81
+ - runner: ubuntu-22.04
82
+ target: ppc64le
83
+ steps:
84
+ - uses: actions/checkout@v6
85
+ with:
86
+ ref: ${{ needs.validate.outputs.sha }}
87
+ - uses: actions/setup-python@v6
88
+ with:
89
+ python-version: "3.14"
90
+ - name: Build wheels
91
+ uses: PyO3/maturin-action@v1
92
+ with:
93
+ target: ${{ matrix.platform.target }}
94
+ args: --release --out dist --find-interpreter
95
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
96
+ manylinux: auto
97
+ - name: Upload wheels
98
+ uses: actions/upload-artifact@v6
99
+ with:
100
+ name: wheels-linux-${{ matrix.platform.target }}
101
+ path: dist
102
+
103
+ musllinux:
104
+ needs: validate
105
+ runs-on: ${{ matrix.platform.runner }}
106
+ strategy:
107
+ matrix:
108
+ platform:
109
+ - runner: ubuntu-22.04
110
+ target: x86_64
111
+ - runner: ubuntu-22.04
112
+ target: x86
113
+ - runner: ubuntu-22.04
114
+ target: aarch64
115
+ - runner: ubuntu-22.04
116
+ target: armv7
117
+ steps:
118
+ - uses: actions/checkout@v6
119
+ with:
120
+ ref: ${{ needs.validate.outputs.sha }}
121
+ - uses: actions/setup-python@v6
122
+ with:
123
+ python-version: "3.14"
124
+ - name: Build wheels
125
+ uses: PyO3/maturin-action@v1
126
+ with:
127
+ target: ${{ matrix.platform.target }}
128
+ args: --release --out dist --find-interpreter
129
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
130
+ manylinux: musllinux_1_2
131
+ - name: Upload wheels
132
+ uses: actions/upload-artifact@v6
133
+ with:
134
+ name: wheels-musllinux-${{ matrix.platform.target }}
135
+ path: dist
136
+
137
+ windows:
138
+ needs: validate
139
+ runs-on: ${{ matrix.runner }}
140
+ strategy:
141
+ matrix:
142
+ include:
143
+ - runner: windows-latest
144
+ target: x64
145
+ rust_target: x86_64-pc-windows-msvc
146
+ python_arch: x64
147
+ python: "3.9"
148
+ - runner: windows-latest
149
+ target: x64
150
+ rust_target: x86_64-pc-windows-msvc
151
+ python_arch: x64
152
+ python: "3.10"
153
+ - runner: windows-latest
154
+ target: x64
155
+ rust_target: x86_64-pc-windows-msvc
156
+ python_arch: x64
157
+ python: "3.11"
158
+ - runner: windows-latest
159
+ target: x64
160
+ rust_target: x86_64-pc-windows-msvc
161
+ python_arch: x64
162
+ python: "3.12"
163
+ - runner: windows-latest
164
+ target: x64
165
+ rust_target: x86_64-pc-windows-msvc
166
+ python_arch: x64
167
+ python: "3.13"
168
+ - runner: windows-latest
169
+ target: x64
170
+ rust_target: x86_64-pc-windows-msvc
171
+ python_arch: x64
172
+ python: "3.14"
173
+ - runner: windows-2022
174
+ target: x64
175
+ rust_target: x86_64-pc-windows-msvc
176
+ python_arch: x64
177
+ python: "3.13"
178
+ - runner: windows-latest
179
+ target: x86
180
+ rust_target: i686-pc-windows-msvc
181
+ python_arch: x86
182
+ python: "3.13"
183
+ - runner: windows-11-arm
184
+ target: aarch64
185
+ rust_target: aarch64-pc-windows-msvc
186
+ python_arch: arm64
187
+ python: "3.13"
188
+ steps:
189
+ - uses: actions/checkout@v6
190
+ with:
191
+ ref: ${{ needs.validate.outputs.sha }}
192
+ - uses: actions/setup-python@v6
193
+ with:
194
+ python-version: ${{ matrix.python }}
195
+ architecture: ${{ matrix.python_arch }}
196
+ - name: Build wheels
197
+ uses: PyO3/maturin-action@v1
198
+ with:
199
+ target: ${{ matrix.target }}
200
+ args: --release --locked --out dist --interpreter python
201
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
202
+ - name: Test native crate
203
+ run: |
204
+ $env:PYO3_PYTHON = python -c "import sys; print(sys.executable)"
205
+ cargo test --locked --target ${{ matrix.rust_target }} --no-default-features
206
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
207
+ - name: Validate wheel contents
208
+ run: python tools/validate_distribution.py dist
209
+ - name: Test installed Windows wheel
210
+ run: |
211
+ python -m pip install "typing-extensions>=4.0"
212
+ python -m pip install --force-reinstall --no-deps --no-index --find-links dist csv_salvage
213
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
214
+ python -m unittest discover -s tests -v
215
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
216
+ - name: Upload wheels
217
+ if: matrix.runner != 'windows-2022'
218
+ uses: actions/upload-artifact@v6
219
+ with:
220
+ name: wheels-windows-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.python }}
221
+ path: dist
222
+
223
+ macos:
224
+ needs: validate
225
+ runs-on: ${{ matrix.platform.runner }}
226
+ strategy:
227
+ matrix:
228
+ platform:
229
+ - runner: macos-15-intel
230
+ target: x86_64
231
+ - runner: macos-latest
232
+ target: aarch64
233
+ steps:
234
+ - uses: actions/checkout@v6
235
+ with:
236
+ ref: ${{ needs.validate.outputs.sha }}
237
+ - uses: actions/setup-python@v6
238
+ with:
239
+ python-version: "3.14"
240
+ - name: Build wheels
241
+ uses: PyO3/maturin-action@v1
242
+ with:
243
+ target: ${{ matrix.platform.target }}
244
+ args: --release --out dist --find-interpreter
245
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
246
+ - name: Upload wheels
247
+ uses: actions/upload-artifact@v6
248
+ with:
249
+ name: wheels-macos-${{ matrix.platform.target }}
250
+ path: dist
251
+
252
+ sdist:
253
+ needs: validate
254
+ runs-on: windows-latest
255
+ steps:
256
+ - uses: actions/checkout@v6
257
+ with:
258
+ ref: ${{ needs.validate.outputs.sha }}
259
+ - uses: actions/setup-python@v6
260
+ with:
261
+ python-version: "3.13"
262
+ - uses: astral-sh/setup-uv@v7
263
+ - uses: PyO3/maturin-action@v1
264
+ with:
265
+ command: sdist
266
+ args: --out dist
267
+ - name: Check distribution metadata
268
+ run: |
269
+ uv sync --locked --group release
270
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
271
+ uv run --group release python -m twine check --strict dist/*
272
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
273
+ python tools/validate_distribution.py dist
274
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
275
+ - name: Rebuild and test source distribution
276
+ run: |
277
+ $source = @(Get-ChildItem dist/*.tar.gz)
278
+ if ($source.Count -ne 1) { throw "Expected one source distribution" }
279
+ python -m pip wheel --no-deps --wheel-dir rebuilt $source[0].FullName
280
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
281
+ python tools/validate_distribution.py rebuilt
282
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
283
+ python -m pip install "typing-extensions>=4.0"
284
+ python -m pip install --force-reinstall --no-deps --no-index --find-links rebuilt csv_salvage
285
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
286
+ python -m unittest discover -s tests -v
287
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
288
+ - uses: actions/upload-artifact@v6
289
+ with:
290
+ name: wheels-sdist
291
+ path: dist
292
+
293
+ publish:
294
+ if: github.event_name == 'release' || startsWith(github.ref, 'refs/tags/v') || inputs.publish
295
+ runs-on: ubuntu-latest
296
+ needs: [linux, musllinux, windows, macos, sdist]
297
+ environment:
298
+ name: pypi
299
+ url: https://pypi.org/p/csv-salvage
300
+ permissions:
301
+ id-token: write
302
+ contents: write
303
+ steps:
304
+ - uses: actions/download-artifact@v7
305
+ - uses: astral-sh/setup-uv@v7
306
+ - name: Publish to PyPI
307
+ run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/ "wheels-*/*"
308
+ - name: Create or update GitHub Release
309
+ uses: softprops/action-gh-release@v2
310
+ if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'release'
311
+ with:
312
+ tag_name: ${{ github.event.release.tag_name || inputs.tag || github.ref_name }}
313
+ files: wheels-*/*
314
+ generate_release_notes: true