pydantic-marshmallow 1.0.0__tar.gz → 1.0.1__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.
- pydantic_marshmallow-1.0.1/.github/workflows/ci.yml +192 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/workflows/release.yml +3 -6
- pydantic_marshmallow-1.0.1/.vscode/settings.json +40 -0
- {pydantic_marshmallow-1.0.0/src/pydantic_marshmallow.egg-info → pydantic_marshmallow-1.0.1}/PKG-INFO +135 -4
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/README.md +132 -1
- pydantic_marshmallow-1.0.1/SECURITY.md +15 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/benchmarks/benchmark_framework.py +1 -1
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/benchmarks/run_benchmarks.py +4 -7
- pydantic_marshmallow-1.0.1/codecov.yml +31 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/changelog.md +3 -5
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/examples.md +1 -1
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/examples/usage.py +15 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/pyproject.toml +38 -2
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/__init__.py +11 -5
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/bridge.py +131 -31
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/type_mapping.py +31 -3
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1/src/pydantic_marshmallow.egg-info}/PKG-INFO +135 -4
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/SOURCES.txt +3 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/requires.txt +2 -2
- pydantic_marshmallow-1.0.1/tests/compatibility/conftest.py +250 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_flask_marshmallow.py +12 -47
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_flask_rebar.py +1 -6
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_webargs.py +1 -12
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_combinations.py +18 -15
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_compatibility.py +10 -15
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/uv.lock +34 -2
- pydantic_marshmallow-1.0.0/.github/workflows/ci.yml +0 -91
- pydantic_marshmallow-1.0.0/SECURITY.md +0 -21
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Change_Size.instructions.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Compatibility_Analysis.instructions.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Coverage.instructions.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Default.instructions.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Git.instructions.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Implementation.instructions.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/workflows/docs.yml +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.gitignore +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.releaserc.json +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/CAPABILITY_MATRIX.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/LICENSE +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/benchmarks/__init__.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/errors.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/hybrid.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/index.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/schema.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/validators.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/getting-started/installation.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/getting-started/quickstart.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/basic-usage.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/ecosystem.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/field-options.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/hooks.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/nested-models.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/index.md +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/mkdocs.yml +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/pydantic-marshmallow.code-workspace +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/setup.cfg +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/errors.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/field_conversion.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/py.typed +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/validators.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/dependency_links.txt +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/top_level.txt +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/__init__.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/__init__.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_apispec.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_connexion.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_flask_smorest.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_marshmallow_dataclass.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_oneofschema.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_sqlalchemy.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/conftest.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_advanced_hooks.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_bridge.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_computed_fields.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_dump_options.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_edge_cases.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_error_handling.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_extended_coverage.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_extended_performance.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_hooks.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_partial_and_unknown.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_performance.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_return_instance.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_schema_parameters.py +0 -0
- {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_validation.py +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: Lint & Type Check
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v5
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
run: uv python install 3.11
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --all-extras
|
|
27
|
+
|
|
28
|
+
- name: Lint with ruff
|
|
29
|
+
run: uv run ruff check src/pydantic_marshmallow/ tests/
|
|
30
|
+
|
|
31
|
+
- name: Lint with flake8
|
|
32
|
+
run: uv run flake8 src/pydantic_marshmallow/ tests/
|
|
33
|
+
|
|
34
|
+
- name: Type check with mypy
|
|
35
|
+
run: uv run mypy src/pydantic_marshmallow/
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
name: Test (Py${{ matrix.python-version }}, ${{ matrix.os }}, MA${{ matrix.marshmallow-version }}, PD${{ matrix.pydantic-version }})
|
|
39
|
+
needs: [lint] # Only run tests after lint passes
|
|
40
|
+
runs-on: ${{ matrix.os }}
|
|
41
|
+
strategy:
|
|
42
|
+
fail-fast: false
|
|
43
|
+
matrix:
|
|
44
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
45
|
+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
|
|
46
|
+
marshmallow-version: ['latest'] # Default to latest Marshmallow (3.x from lock)
|
|
47
|
+
pydantic-version: ['latest'] # Default to latest Pydantic
|
|
48
|
+
include:
|
|
49
|
+
# === BASE VERSIONS (minimum supported) ===
|
|
50
|
+
# Marshmallow 3.18.0 + Pydantic 2.0.0 - minimum supported combo
|
|
51
|
+
- os: ubuntu-latest
|
|
52
|
+
python-version: '3.10'
|
|
53
|
+
marshmallow-version: '3.18.0'
|
|
54
|
+
pydantic-version: '2.0.0'
|
|
55
|
+
# Pydantic 2.0.0 with latest Marshmallow 3.x
|
|
56
|
+
- os: ubuntu-latest
|
|
57
|
+
python-version: '3.11'
|
|
58
|
+
marshmallow-version: 'latest'
|
|
59
|
+
pydantic-version: '2.0.0'
|
|
60
|
+
|
|
61
|
+
# === INTERMEDIATE VERSIONS ===
|
|
62
|
+
# Marshmallow 3.21.0 (last 3.x before 4.0) + Pydantic 2.5.x
|
|
63
|
+
- os: ubuntu-latest
|
|
64
|
+
python-version: '3.11'
|
|
65
|
+
marshmallow-version: '3.21.0'
|
|
66
|
+
pydantic-version: '2.5'
|
|
67
|
+
# Pydantic 2.5 (significant validation changes) with latest Marshmallow
|
|
68
|
+
- os: ubuntu-latest
|
|
69
|
+
python-version: '3.12'
|
|
70
|
+
marshmallow-version: 'latest'
|
|
71
|
+
pydantic-version: '2.5'
|
|
72
|
+
|
|
73
|
+
# === LATEST/BLEEDING EDGE ===
|
|
74
|
+
# Marshmallow 4.x latest on Ubuntu with Python 3.11 and 3.12
|
|
75
|
+
- os: ubuntu-latest
|
|
76
|
+
python-version: '3.11'
|
|
77
|
+
marshmallow-version: '4-latest'
|
|
78
|
+
pydantic-version: 'latest'
|
|
79
|
+
- os: ubuntu-latest
|
|
80
|
+
python-version: '3.12'
|
|
81
|
+
marshmallow-version: '4-latest'
|
|
82
|
+
pydantic-version: 'latest'
|
|
83
|
+
# Marshmallow 4.x + Pydantic 2.5 cross-version
|
|
84
|
+
- os: ubuntu-latest
|
|
85
|
+
python-version: '3.11'
|
|
86
|
+
marshmallow-version: '4-latest'
|
|
87
|
+
pydantic-version: '2.5'
|
|
88
|
+
exclude:
|
|
89
|
+
# Reduce matrix size - test all Python versions on Ubuntu, latest on others
|
|
90
|
+
- os: windows-latest
|
|
91
|
+
python-version: '3.10'
|
|
92
|
+
- os: windows-latest
|
|
93
|
+
python-version: '3.13'
|
|
94
|
+
- os: windows-latest
|
|
95
|
+
python-version: '3.14'
|
|
96
|
+
- os: macos-latest
|
|
97
|
+
python-version: '3.10'
|
|
98
|
+
- os: macos-latest
|
|
99
|
+
python-version: '3.13'
|
|
100
|
+
- os: macos-latest
|
|
101
|
+
python-version: '3.14'
|
|
102
|
+
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@v4
|
|
105
|
+
|
|
106
|
+
- name: Install uv
|
|
107
|
+
uses: astral-sh/setup-uv@v5
|
|
108
|
+
|
|
109
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
110
|
+
run: uv python install ${{ matrix.python-version }}
|
|
111
|
+
|
|
112
|
+
- name: Install dependencies
|
|
113
|
+
run: uv sync --all-extras
|
|
114
|
+
|
|
115
|
+
- name: Override Marshmallow to 4.x latest
|
|
116
|
+
if: matrix.marshmallow-version == '4-latest'
|
|
117
|
+
run: uv pip install --upgrade "marshmallow>=4.0.0"
|
|
118
|
+
|
|
119
|
+
- name: Override Marshmallow to specific version
|
|
120
|
+
if: matrix.marshmallow-version != 'latest' && matrix.marshmallow-version != '4-latest'
|
|
121
|
+
run: uv pip install "marshmallow==${{ matrix.marshmallow-version }}"
|
|
122
|
+
|
|
123
|
+
- name: Override Pydantic to 2.5.x
|
|
124
|
+
if: matrix.pydantic-version == '2.5'
|
|
125
|
+
run: uv pip install "pydantic>=2.5.0,<2.6.0"
|
|
126
|
+
|
|
127
|
+
- name: Override Pydantic to specific version
|
|
128
|
+
if: matrix.pydantic-version == '2.0.0'
|
|
129
|
+
run: uv pip install "pydantic>=2.0.0,<2.1.0"
|
|
130
|
+
|
|
131
|
+
- name: Run tests
|
|
132
|
+
run: uv run pytest tests/ -v --tb=short
|
|
133
|
+
|
|
134
|
+
coverage:
|
|
135
|
+
name: Coverage
|
|
136
|
+
needs: [lint]
|
|
137
|
+
runs-on: ubuntu-latest
|
|
138
|
+
steps:
|
|
139
|
+
- uses: actions/checkout@v4
|
|
140
|
+
with:
|
|
141
|
+
fetch-depth: 0 # Fetch all history for diff-cover
|
|
142
|
+
|
|
143
|
+
- name: Install uv
|
|
144
|
+
uses: astral-sh/setup-uv@v5
|
|
145
|
+
|
|
146
|
+
- name: Set up Python
|
|
147
|
+
run: uv python install 3.11
|
|
148
|
+
|
|
149
|
+
- name: Install dependencies
|
|
150
|
+
run: uv sync --all-extras
|
|
151
|
+
|
|
152
|
+
- name: Run tests with coverage
|
|
153
|
+
run: |
|
|
154
|
+
uv run pytest tests/ --cov=src/pydantic_marshmallow --cov-report=xml --cov-report=term
|
|
155
|
+
|
|
156
|
+
- name: Upload coverage to Codecov
|
|
157
|
+
uses: codecov/codecov-action@v4
|
|
158
|
+
with:
|
|
159
|
+
files: ./coverage.xml
|
|
160
|
+
fail_ci_if_error: false # Don't fail if Codecov is down
|
|
161
|
+
verbose: true
|
|
162
|
+
env:
|
|
163
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
164
|
+
|
|
165
|
+
- name: Check diff coverage (changed lines)
|
|
166
|
+
run: |
|
|
167
|
+
uv run diff-cover coverage.xml --compare-branch=origin/main --fail-under=80
|
|
168
|
+
continue-on-error: true # Warn but don't fail initially
|
|
169
|
+
|
|
170
|
+
# This job is used as the required status check for branch protection
|
|
171
|
+
# Configure GitHub: Settings > Branches > Branch protection > Require "CI Passed"
|
|
172
|
+
ci-passed:
|
|
173
|
+
name: CI Passed
|
|
174
|
+
runs-on: ubuntu-latest
|
|
175
|
+
needs: [lint, test, coverage]
|
|
176
|
+
if: always()
|
|
177
|
+
steps:
|
|
178
|
+
- name: Check all jobs passed
|
|
179
|
+
run: |
|
|
180
|
+
if [[ "${{ needs.lint.result }}" != "success" ]]; then
|
|
181
|
+
echo "❌ Lint job failed"
|
|
182
|
+
exit 1
|
|
183
|
+
fi
|
|
184
|
+
if [[ "${{ needs.test.result }}" != "success" ]]; then
|
|
185
|
+
echo "❌ Test job failed"
|
|
186
|
+
exit 1
|
|
187
|
+
fi
|
|
188
|
+
if [[ "${{ needs.coverage.result }}" != "success" ]]; then
|
|
189
|
+
echo "❌ Coverage job failed"
|
|
190
|
+
exit 1
|
|
191
|
+
fi
|
|
192
|
+
echo "✅ All CI checks passed!"
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
name: Release
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
4
|
workflow_dispatch:
|
|
8
5
|
inputs:
|
|
9
6
|
dry_run:
|
|
@@ -39,7 +36,7 @@ jobs:
|
|
|
39
36
|
id: semantic
|
|
40
37
|
uses: cycjimmy/semantic-release-action@v4
|
|
41
38
|
with:
|
|
42
|
-
dry_run: ${{ inputs.dry_run
|
|
39
|
+
dry_run: ${{ inputs.dry_run }}
|
|
43
40
|
extra_plugins: |
|
|
44
41
|
conventional-changelog-conventionalcommits
|
|
45
42
|
env:
|
|
@@ -48,7 +45,7 @@ jobs:
|
|
|
48
45
|
build:
|
|
49
46
|
name: Build Package
|
|
50
47
|
needs: release
|
|
51
|
-
if: needs.release.outputs.new_release_published == 'true' &&
|
|
48
|
+
if: needs.release.outputs.new_release_published == 'true' && inputs.dry_run == false
|
|
52
49
|
runs-on: ubuntu-latest
|
|
53
50
|
steps:
|
|
54
51
|
- name: Checkout
|
|
@@ -72,7 +69,7 @@ jobs:
|
|
|
72
69
|
publish:
|
|
73
70
|
name: Publish to PyPI
|
|
74
71
|
needs: [release, build]
|
|
75
|
-
if: needs.release.outputs.new_release_published == 'true' &&
|
|
72
|
+
if: needs.release.outputs.new_release_published == 'true' && inputs.dry_run == false
|
|
76
73
|
runs-on: ubuntu-latest
|
|
77
74
|
environment: pypi # Requires approval if configured in GitHub Settings
|
|
78
75
|
permissions:
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"python.analysis.extraPaths": [
|
|
3
|
+
"src"
|
|
4
|
+
],
|
|
5
|
+
"python.analysis.include": [
|
|
6
|
+
"src/**/*.py"
|
|
7
|
+
],
|
|
8
|
+
"python.analysis.exclude": [
|
|
9
|
+
"tests/**",
|
|
10
|
+
"benchmarks/**",
|
|
11
|
+
"examples/**",
|
|
12
|
+
"**/__pycache__/**",
|
|
13
|
+
".venv/**"
|
|
14
|
+
],
|
|
15
|
+
"python.analysis.ignore": [
|
|
16
|
+
"tests/**",
|
|
17
|
+
"benchmarks/**",
|
|
18
|
+
"examples/**"
|
|
19
|
+
],
|
|
20
|
+
"python.analysis.diagnosticMode": "workspace",
|
|
21
|
+
"mypy-type-checker.args": [
|
|
22
|
+
"src/pydantic_marshmallow"
|
|
23
|
+
],
|
|
24
|
+
"flake8.args": [
|
|
25
|
+
"src/pydantic_marshmallow"
|
|
26
|
+
],
|
|
27
|
+
"[python]": {
|
|
28
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
29
|
+
"editor.formatOnSave": true,
|
|
30
|
+
"editor.codeActionsOnSave": {
|
|
31
|
+
"source.organizeImports": "explicit",
|
|
32
|
+
"source.fixAll.autopep8": "never",
|
|
33
|
+
"source.fixAll.ruff": "explicit"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"autopep8.enable": false,
|
|
37
|
+
"python.formatting.provider": "none",
|
|
38
|
+
"ruff.format.args": [],
|
|
39
|
+
"ruff.organizeImports": true
|
|
40
|
+
}
|
{pydantic_marshmallow-1.0.0/src/pydantic_marshmallow.egg-info → pydantic_marshmallow-1.0.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-marshmallow
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Bring Pydantic's power to Marshmallow: type annotations, validators, and automatic schema generation
|
|
5
5
|
Author-email: Your Name <your.email@example.com>
|
|
6
6
|
License: MIT
|
|
@@ -24,8 +24,8 @@ Classifier: Typing :: Typed
|
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
|
-
Requires-Dist: marshmallow
|
|
28
|
-
Requires-Dist: pydantic
|
|
27
|
+
Requires-Dist: marshmallow<5.0.0,>=3.18.0
|
|
28
|
+
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
29
29
|
Requires-Dist: typing-extensions>=4.0.0
|
|
30
30
|
Requires-Dist: orjson>=3.9.0
|
|
31
31
|
Provides-Extra: dev
|
|
@@ -67,6 +67,29 @@ Bridge Pydantic's power with Marshmallow's ecosystem. Use Pydantic models for va
|
|
|
67
67
|
|
|
68
68
|
📖 **[Documentation](https://mockodin.github.io/pydantic-marshmallow)** | 🐙 **[GitHub](https://github.com/mockodin/pydantic-marshmallow)**
|
|
69
69
|
|
|
70
|
+
## Why pydantic-marshmallow?
|
|
71
|
+
|
|
72
|
+
Get the best of both worlds: **Pydantic's speed** with **Marshmallow's ecosystem**.
|
|
73
|
+
|
|
74
|
+
### Performance
|
|
75
|
+
|
|
76
|
+
pydantic-marshmallow uses Pydantic's Rust-powered validation engine under the hood, delivering significant performance improvements over native Marshmallow—especially for nested data structures:
|
|
77
|
+
|
|
78
|
+
| Operation | pydantic-marshmallow | Marshmallow | Speedup |
|
|
79
|
+
|-----------|---------------------|-------------|---------|
|
|
80
|
+
| Simple load | 3.0 µs | 5.3 µs | **1.8x faster** |
|
|
81
|
+
| Nested model | 3.5 µs | 11.6 µs | **3.3x faster** |
|
|
82
|
+
| Deep nested (4 levels) | 5.6 µs | 32.3 µs | **5.8x faster** |
|
|
83
|
+
| Batch (100 items) | 255 µs | 474 µs | **1.9x faster** |
|
|
84
|
+
|
|
85
|
+
*Benchmarks run on Python 3.11. Run `python -m benchmarks.run_benchmarks` to reproduce.*
|
|
86
|
+
|
|
87
|
+
### Why it matters
|
|
88
|
+
|
|
89
|
+
- **Existing Marshmallow projects**: Incrementally adopt Pydantic validation without rewriting your API layer
|
|
90
|
+
- **Flask/webargs/apispec users**: Keep your integrations, get faster validation
|
|
91
|
+
- **Performance-sensitive APIs**: Nested model validation is 3-6x faster than native Marshmallow
|
|
92
|
+
|
|
70
93
|
## Features
|
|
71
94
|
|
|
72
95
|
- **Pydantic Validation**: Leverage Pydantic's Rust-powered validation engine
|
|
@@ -213,7 +236,7 @@ class User(BaseModel):
|
|
|
213
236
|
first: str
|
|
214
237
|
last: str
|
|
215
238
|
|
|
216
|
-
@computed_field
|
|
239
|
+
@computed_field # type: ignore[misc]
|
|
217
240
|
@property
|
|
218
241
|
def full_name(self) -> str:
|
|
219
242
|
return f"{self.first} {self.last}"
|
|
@@ -237,6 +260,21 @@ schema.dump(user, exclude_unset=True)
|
|
|
237
260
|
schema.dump(user, exclude_defaults=True)
|
|
238
261
|
```
|
|
239
262
|
|
|
263
|
+
## JSON Serialization
|
|
264
|
+
|
|
265
|
+
Direct JSON string support:
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
# Deserialize from JSON string
|
|
269
|
+
user = schema.loads('{"name": "Alice", "email": "alice@example.com", "age": 30}')
|
|
270
|
+
|
|
271
|
+
# Serialize to JSON string
|
|
272
|
+
json_str = schema.dumps(user)
|
|
273
|
+
|
|
274
|
+
# Batch operations
|
|
275
|
+
users = schema.loads('[{"name": "Alice", ...}, {"name": "Bob", ...}]', many=True)
|
|
276
|
+
```
|
|
277
|
+
|
|
240
278
|
## Flask-Marshmallow Integration
|
|
241
279
|
|
|
242
280
|
```python
|
|
@@ -288,6 +326,99 @@ spec = APISpec(
|
|
|
288
326
|
spec.components.schema("User", schema=UserSchema)
|
|
289
327
|
```
|
|
290
328
|
|
|
329
|
+
## flask-smorest Integration
|
|
330
|
+
|
|
331
|
+
Build REST APIs with automatic OpenAPI documentation:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
from flask import Flask
|
|
335
|
+
from flask_smorest import Api, Blueprint
|
|
336
|
+
from pydantic import BaseModel, Field
|
|
337
|
+
from pydantic_marshmallow import schema_for
|
|
338
|
+
|
|
339
|
+
app = Flask(__name__)
|
|
340
|
+
app.config["API_TITLE"] = "My API"
|
|
341
|
+
app.config["API_VERSION"] = "v1"
|
|
342
|
+
app.config["OPENAPI_VERSION"] = "3.0.2"
|
|
343
|
+
|
|
344
|
+
api = Api(app)
|
|
345
|
+
blp = Blueprint("users", __name__, url_prefix="/users")
|
|
346
|
+
|
|
347
|
+
class UserCreate(BaseModel):
|
|
348
|
+
name: str = Field(min_length=1)
|
|
349
|
+
email: str
|
|
350
|
+
|
|
351
|
+
UserCreateSchema = schema_for(UserCreate)
|
|
352
|
+
UserSchema = schema_for(User)
|
|
353
|
+
|
|
354
|
+
@blp.post("/")
|
|
355
|
+
@blp.arguments(UserCreateSchema)
|
|
356
|
+
@blp.response(201, UserSchema)
|
|
357
|
+
def create_user(data):
|
|
358
|
+
# data is a Pydantic UserCreate instance
|
|
359
|
+
user = User(id=1, name=data.name, email=data.email)
|
|
360
|
+
return UserSchema().dump(user)
|
|
361
|
+
|
|
362
|
+
api.register_blueprint(blp)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## flask-rebar Integration
|
|
366
|
+
|
|
367
|
+
Build REST APIs with automatic Swagger documentation:
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
from flask import Flask
|
|
371
|
+
from flask_rebar import Rebar, get_validated_body
|
|
372
|
+
from pydantic_marshmallow import schema_for
|
|
373
|
+
|
|
374
|
+
app = Flask(__name__)
|
|
375
|
+
rebar = Rebar()
|
|
376
|
+
registry = rebar.create_handler_registry()
|
|
377
|
+
|
|
378
|
+
UserCreateSchema = schema_for(UserCreate)
|
|
379
|
+
UserSchema = schema_for(User)
|
|
380
|
+
|
|
381
|
+
@registry.handles(
|
|
382
|
+
rule="/users",
|
|
383
|
+
method="POST",
|
|
384
|
+
request_body_schema=UserCreateSchema(),
|
|
385
|
+
response_body_schema=UserSchema(),
|
|
386
|
+
)
|
|
387
|
+
def create_user():
|
|
388
|
+
data = get_validated_body() # Pydantic UserCreate instance
|
|
389
|
+
user = User(id=1, name=data.name, email=data.email)
|
|
390
|
+
return UserSchema().dump(user)
|
|
391
|
+
|
|
392
|
+
rebar.init_app(app)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## SQLAlchemy Pattern
|
|
396
|
+
|
|
397
|
+
Use Pydantic for API validation alongside SQLAlchemy ORM:
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
from sqlalchemy.orm import Session
|
|
401
|
+
from pydantic_marshmallow import schema_for
|
|
402
|
+
|
|
403
|
+
# Pydantic model for API validation
|
|
404
|
+
class UserCreate(BaseModel):
|
|
405
|
+
name: str = Field(min_length=1)
|
|
406
|
+
email: str
|
|
407
|
+
|
|
408
|
+
UserCreateSchema = schema_for(UserCreate)
|
|
409
|
+
|
|
410
|
+
def create_user(session: Session, data: dict):
|
|
411
|
+
# Validate API input with Pydantic
|
|
412
|
+
schema = UserCreateSchema()
|
|
413
|
+
validated = schema.load(data) # Returns Pydantic model
|
|
414
|
+
|
|
415
|
+
# Create ORM object from validated data
|
|
416
|
+
orm_user = UserModel(name=validated.name, email=validated.email)
|
|
417
|
+
session.add(orm_user)
|
|
418
|
+
session.commit()
|
|
419
|
+
return orm_user
|
|
420
|
+
```
|
|
421
|
+
|
|
291
422
|
## HybridModel
|
|
292
423
|
|
|
293
424
|
For models that need both Pydantic and Marshmallow APIs:
|
|
@@ -9,6 +9,29 @@ Bridge Pydantic's power with Marshmallow's ecosystem. Use Pydantic models for va
|
|
|
9
9
|
|
|
10
10
|
📖 **[Documentation](https://mockodin.github.io/pydantic-marshmallow)** | 🐙 **[GitHub](https://github.com/mockodin/pydantic-marshmallow)**
|
|
11
11
|
|
|
12
|
+
## Why pydantic-marshmallow?
|
|
13
|
+
|
|
14
|
+
Get the best of both worlds: **Pydantic's speed** with **Marshmallow's ecosystem**.
|
|
15
|
+
|
|
16
|
+
### Performance
|
|
17
|
+
|
|
18
|
+
pydantic-marshmallow uses Pydantic's Rust-powered validation engine under the hood, delivering significant performance improvements over native Marshmallow—especially for nested data structures:
|
|
19
|
+
|
|
20
|
+
| Operation | pydantic-marshmallow | Marshmallow | Speedup |
|
|
21
|
+
|-----------|---------------------|-------------|---------|
|
|
22
|
+
| Simple load | 3.0 µs | 5.3 µs | **1.8x faster** |
|
|
23
|
+
| Nested model | 3.5 µs | 11.6 µs | **3.3x faster** |
|
|
24
|
+
| Deep nested (4 levels) | 5.6 µs | 32.3 µs | **5.8x faster** |
|
|
25
|
+
| Batch (100 items) | 255 µs | 474 µs | **1.9x faster** |
|
|
26
|
+
|
|
27
|
+
*Benchmarks run on Python 3.11. Run `python -m benchmarks.run_benchmarks` to reproduce.*
|
|
28
|
+
|
|
29
|
+
### Why it matters
|
|
30
|
+
|
|
31
|
+
- **Existing Marshmallow projects**: Incrementally adopt Pydantic validation without rewriting your API layer
|
|
32
|
+
- **Flask/webargs/apispec users**: Keep your integrations, get faster validation
|
|
33
|
+
- **Performance-sensitive APIs**: Nested model validation is 3-6x faster than native Marshmallow
|
|
34
|
+
|
|
12
35
|
## Features
|
|
13
36
|
|
|
14
37
|
- **Pydantic Validation**: Leverage Pydantic's Rust-powered validation engine
|
|
@@ -155,7 +178,7 @@ class User(BaseModel):
|
|
|
155
178
|
first: str
|
|
156
179
|
last: str
|
|
157
180
|
|
|
158
|
-
@computed_field
|
|
181
|
+
@computed_field # type: ignore[misc]
|
|
159
182
|
@property
|
|
160
183
|
def full_name(self) -> str:
|
|
161
184
|
return f"{self.first} {self.last}"
|
|
@@ -179,6 +202,21 @@ schema.dump(user, exclude_unset=True)
|
|
|
179
202
|
schema.dump(user, exclude_defaults=True)
|
|
180
203
|
```
|
|
181
204
|
|
|
205
|
+
## JSON Serialization
|
|
206
|
+
|
|
207
|
+
Direct JSON string support:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
# Deserialize from JSON string
|
|
211
|
+
user = schema.loads('{"name": "Alice", "email": "alice@example.com", "age": 30}')
|
|
212
|
+
|
|
213
|
+
# Serialize to JSON string
|
|
214
|
+
json_str = schema.dumps(user)
|
|
215
|
+
|
|
216
|
+
# Batch operations
|
|
217
|
+
users = schema.loads('[{"name": "Alice", ...}, {"name": "Bob", ...}]', many=True)
|
|
218
|
+
```
|
|
219
|
+
|
|
182
220
|
## Flask-Marshmallow Integration
|
|
183
221
|
|
|
184
222
|
```python
|
|
@@ -230,6 +268,99 @@ spec = APISpec(
|
|
|
230
268
|
spec.components.schema("User", schema=UserSchema)
|
|
231
269
|
```
|
|
232
270
|
|
|
271
|
+
## flask-smorest Integration
|
|
272
|
+
|
|
273
|
+
Build REST APIs with automatic OpenAPI documentation:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from flask import Flask
|
|
277
|
+
from flask_smorest import Api, Blueprint
|
|
278
|
+
from pydantic import BaseModel, Field
|
|
279
|
+
from pydantic_marshmallow import schema_for
|
|
280
|
+
|
|
281
|
+
app = Flask(__name__)
|
|
282
|
+
app.config["API_TITLE"] = "My API"
|
|
283
|
+
app.config["API_VERSION"] = "v1"
|
|
284
|
+
app.config["OPENAPI_VERSION"] = "3.0.2"
|
|
285
|
+
|
|
286
|
+
api = Api(app)
|
|
287
|
+
blp = Blueprint("users", __name__, url_prefix="/users")
|
|
288
|
+
|
|
289
|
+
class UserCreate(BaseModel):
|
|
290
|
+
name: str = Field(min_length=1)
|
|
291
|
+
email: str
|
|
292
|
+
|
|
293
|
+
UserCreateSchema = schema_for(UserCreate)
|
|
294
|
+
UserSchema = schema_for(User)
|
|
295
|
+
|
|
296
|
+
@blp.post("/")
|
|
297
|
+
@blp.arguments(UserCreateSchema)
|
|
298
|
+
@blp.response(201, UserSchema)
|
|
299
|
+
def create_user(data):
|
|
300
|
+
# data is a Pydantic UserCreate instance
|
|
301
|
+
user = User(id=1, name=data.name, email=data.email)
|
|
302
|
+
return UserSchema().dump(user)
|
|
303
|
+
|
|
304
|
+
api.register_blueprint(blp)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## flask-rebar Integration
|
|
308
|
+
|
|
309
|
+
Build REST APIs with automatic Swagger documentation:
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
from flask import Flask
|
|
313
|
+
from flask_rebar import Rebar, get_validated_body
|
|
314
|
+
from pydantic_marshmallow import schema_for
|
|
315
|
+
|
|
316
|
+
app = Flask(__name__)
|
|
317
|
+
rebar = Rebar()
|
|
318
|
+
registry = rebar.create_handler_registry()
|
|
319
|
+
|
|
320
|
+
UserCreateSchema = schema_for(UserCreate)
|
|
321
|
+
UserSchema = schema_for(User)
|
|
322
|
+
|
|
323
|
+
@registry.handles(
|
|
324
|
+
rule="/users",
|
|
325
|
+
method="POST",
|
|
326
|
+
request_body_schema=UserCreateSchema(),
|
|
327
|
+
response_body_schema=UserSchema(),
|
|
328
|
+
)
|
|
329
|
+
def create_user():
|
|
330
|
+
data = get_validated_body() # Pydantic UserCreate instance
|
|
331
|
+
user = User(id=1, name=data.name, email=data.email)
|
|
332
|
+
return UserSchema().dump(user)
|
|
333
|
+
|
|
334
|
+
rebar.init_app(app)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## SQLAlchemy Pattern
|
|
338
|
+
|
|
339
|
+
Use Pydantic for API validation alongside SQLAlchemy ORM:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
from sqlalchemy.orm import Session
|
|
343
|
+
from pydantic_marshmallow import schema_for
|
|
344
|
+
|
|
345
|
+
# Pydantic model for API validation
|
|
346
|
+
class UserCreate(BaseModel):
|
|
347
|
+
name: str = Field(min_length=1)
|
|
348
|
+
email: str
|
|
349
|
+
|
|
350
|
+
UserCreateSchema = schema_for(UserCreate)
|
|
351
|
+
|
|
352
|
+
def create_user(session: Session, data: dict):
|
|
353
|
+
# Validate API input with Pydantic
|
|
354
|
+
schema = UserCreateSchema()
|
|
355
|
+
validated = schema.load(data) # Returns Pydantic model
|
|
356
|
+
|
|
357
|
+
# Create ORM object from validated data
|
|
358
|
+
orm_user = UserModel(name=validated.name, email=validated.email)
|
|
359
|
+
session.add(orm_user)
|
|
360
|
+
session.commit()
|
|
361
|
+
return orm_user
|
|
362
|
+
```
|
|
363
|
+
|
|
233
364
|
## HybridModel
|
|
234
365
|
|
|
235
366
|
For models that need both Pydantic and Marshmallow APIs:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 1.0.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
Please report security vulnerabilities by opening a
|
|
12
|
+
[private security advisory](https://github.com/mockodin/pydantic-marshmallow/security/advisories/new)
|
|
13
|
+
or emailing the maintainers directly.
|
|
14
|
+
|
|
15
|
+
Do not open public issues for security vulnerabilities.
|
|
@@ -362,7 +362,7 @@ class BenchmarkSuite:
|
|
|
362
362
|
"""
|
|
363
363
|
results: dict[str, BenchmarkResult] = {}
|
|
364
364
|
|
|
365
|
-
benchmarks = self._benchmarks.items()
|
|
365
|
+
benchmarks = list(self._benchmarks.items())
|
|
366
366
|
if filter_pattern:
|
|
367
367
|
benchmarks = [(n, b) for n, b in benchmarks if filter_pattern in n]
|
|
368
368
|
|
|
@@ -21,6 +21,7 @@ Usage:
|
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
23
|
import argparse
|
|
24
|
+
import contextlib
|
|
24
25
|
import sys
|
|
25
26
|
from enum import Enum
|
|
26
27
|
from pathlib import Path
|
|
@@ -87,7 +88,7 @@ class UserWithComputedField(BaseModel):
|
|
|
87
88
|
last: str
|
|
88
89
|
age: int
|
|
89
90
|
|
|
90
|
-
@computed_field
|
|
91
|
+
@computed_field # type: ignore[misc]
|
|
91
92
|
@property
|
|
92
93
|
def full_name(self) -> str:
|
|
93
94
|
return f"{self.first} {self.last}"
|
|
@@ -696,20 +697,16 @@ def create_error_suite() -> BenchmarkSuite:
|
|
|
696
697
|
|
|
697
698
|
@suite.add("validation_error_bridge")
|
|
698
699
|
def bench_error_bridge():
|
|
699
|
-
|
|
700
|
+
with contextlib.suppress(Exception):
|
|
700
701
|
bridge_schema.load(INVALID_DATA)
|
|
701
|
-
except Exception:
|
|
702
|
-
pass
|
|
703
702
|
|
|
704
703
|
# Marshmallow validation error
|
|
705
704
|
ma_schema = SimpleUserMarshmallow()
|
|
706
705
|
|
|
707
706
|
@suite.add("validation_error_marshmallow")
|
|
708
707
|
def bench_error_marshmallow():
|
|
709
|
-
|
|
708
|
+
with contextlib.suppress(Exception):
|
|
710
709
|
ma_schema.load(INVALID_DATA)
|
|
711
|
-
except Exception:
|
|
712
|
-
pass
|
|
713
710
|
|
|
714
711
|
return suite
|
|
715
712
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Codecov configuration
|
|
2
|
+
# https://docs.codecov.com/docs/codecov-yaml
|
|
3
|
+
|
|
4
|
+
coverage:
|
|
5
|
+
status:
|
|
6
|
+
# Overall project coverage
|
|
7
|
+
project:
|
|
8
|
+
default:
|
|
9
|
+
target: 80% # Minimum overall coverage
|
|
10
|
+
threshold: 2% # Allow up to 2% drop from base
|
|
11
|
+
|
|
12
|
+
# Coverage for changed lines only (enforces new code coverage)
|
|
13
|
+
patch:
|
|
14
|
+
default:
|
|
15
|
+
target: 90% # New/changed code must have 90% coverage
|
|
16
|
+
|
|
17
|
+
# Don't wait for all CI jobs
|
|
18
|
+
precision: 2
|
|
19
|
+
round: down
|
|
20
|
+
|
|
21
|
+
# Ignore test files and benchmarks from coverage
|
|
22
|
+
ignore:
|
|
23
|
+
- "tests/**/*"
|
|
24
|
+
- "benchmarks/**/*"
|
|
25
|
+
- "docs/**/*"
|
|
26
|
+
|
|
27
|
+
# Comment on PRs with coverage report
|
|
28
|
+
comment:
|
|
29
|
+
layout: "reach,diff,flags,files"
|
|
30
|
+
behavior: default
|
|
31
|
+
require_changes: true
|