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.
Files changed (85) hide show
  1. pydantic_marshmallow-1.0.1/.github/workflows/ci.yml +192 -0
  2. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/workflows/release.yml +3 -6
  3. pydantic_marshmallow-1.0.1/.vscode/settings.json +40 -0
  4. {pydantic_marshmallow-1.0.0/src/pydantic_marshmallow.egg-info → pydantic_marshmallow-1.0.1}/PKG-INFO +135 -4
  5. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/README.md +132 -1
  6. pydantic_marshmallow-1.0.1/SECURITY.md +15 -0
  7. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/benchmarks/benchmark_framework.py +1 -1
  8. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/benchmarks/run_benchmarks.py +4 -7
  9. pydantic_marshmallow-1.0.1/codecov.yml +31 -0
  10. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/changelog.md +3 -5
  11. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/examples.md +1 -1
  12. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/examples/usage.py +15 -0
  13. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/pyproject.toml +38 -2
  14. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/__init__.py +11 -5
  15. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/bridge.py +131 -31
  16. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/type_mapping.py +31 -3
  17. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1/src/pydantic_marshmallow.egg-info}/PKG-INFO +135 -4
  18. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/SOURCES.txt +3 -0
  19. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/requires.txt +2 -2
  20. pydantic_marshmallow-1.0.1/tests/compatibility/conftest.py +250 -0
  21. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_flask_marshmallow.py +12 -47
  22. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_flask_rebar.py +1 -6
  23. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_webargs.py +1 -12
  24. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_combinations.py +18 -15
  25. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_compatibility.py +10 -15
  26. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/uv.lock +34 -2
  27. pydantic_marshmallow-1.0.0/.github/workflows/ci.yml +0 -91
  28. pydantic_marshmallow-1.0.0/SECURITY.md +0 -21
  29. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Change_Size.instructions.md +0 -0
  30. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Compatibility_Analysis.instructions.md +0 -0
  31. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Coverage.instructions.md +0 -0
  32. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Default.instructions.md +0 -0
  33. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Git.instructions.md +0 -0
  34. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/instructions/Implementation.instructions.md +0 -0
  35. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.github/workflows/docs.yml +0 -0
  36. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.gitignore +0 -0
  37. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/.releaserc.json +0 -0
  38. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/CAPABILITY_MATRIX.md +0 -0
  39. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/LICENSE +0 -0
  40. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/benchmarks/__init__.py +0 -0
  41. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/errors.md +0 -0
  42. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/hybrid.md +0 -0
  43. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/index.md +0 -0
  44. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/schema.md +0 -0
  45. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/api/validators.md +0 -0
  46. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/getting-started/installation.md +0 -0
  47. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/getting-started/quickstart.md +0 -0
  48. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/basic-usage.md +0 -0
  49. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/ecosystem.md +0 -0
  50. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/field-options.md +0 -0
  51. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/hooks.md +0 -0
  52. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/guide/nested-models.md +0 -0
  53. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/docs/index.md +0 -0
  54. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/mkdocs.yml +0 -0
  55. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/pydantic-marshmallow.code-workspace +0 -0
  56. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/setup.cfg +0 -0
  57. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/errors.py +0 -0
  58. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/field_conversion.py +0 -0
  59. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/py.typed +0 -0
  60. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow/validators.py +0 -0
  61. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/dependency_links.txt +0 -0
  62. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/src/pydantic_marshmallow.egg-info/top_level.txt +0 -0
  63. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/__init__.py +0 -0
  64. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/__init__.py +0 -0
  65. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_apispec.py +0 -0
  66. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_connexion.py +0 -0
  67. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_flask_smorest.py +0 -0
  68. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_marshmallow_dataclass.py +0 -0
  69. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_oneofschema.py +0 -0
  70. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/compatibility/test_sqlalchemy.py +0 -0
  71. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/conftest.py +0 -0
  72. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_advanced_hooks.py +0 -0
  73. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_bridge.py +0 -0
  74. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_computed_fields.py +0 -0
  75. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_dump_options.py +0 -0
  76. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_edge_cases.py +0 -0
  77. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_error_handling.py +0 -0
  78. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_extended_coverage.py +0 -0
  79. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_extended_performance.py +0 -0
  80. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_hooks.py +0 -0
  81. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_partial_and_unknown.py +0 -0
  82. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_performance.py +0 -0
  83. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_return_instance.py +0 -0
  84. {pydantic_marshmallow-1.0.0 → pydantic_marshmallow-1.0.1}/tests/test_schema_parameters.py +0 -0
  85. {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 || false }}
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' && (inputs.dry_run == false || inputs.dry_run == null)
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' && (inputs.dry_run == false || inputs.dry_run == null)
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
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-marshmallow
3
- Version: 1.0.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>=3.18.0
28
- Requires-Dist: pydantic>=2.0.0
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
- try:
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
- try:
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