Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pydantic for openSUSE:Factory
checked in at 2026-05-12 19:26:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pydantic (Old)
and /work/SRC/openSUSE:Factory/.python-pydantic.new.1966 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pydantic"
Tue May 12 19:26:11 2026 rev:40 rq:1352320 version:2.13.4
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pydantic/python-pydantic.changes
2026-04-18 21:32:20.475715056 +0200
+++
/work/SRC/openSUSE:Factory/.python-pydantic.new.1966/python-pydantic.changes
2026-05-12 19:26:31.690738734 +0200
@@ -1,0 +2,13 @@
+Sun May 10 19:57:53 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 2.13.4:
+ * Bump libc from 0.2.155 to 0.2.185 by @Viicos in #13109
+ * Adapt `pydantic-core` linker flags on macOS by @washingtoneg
+ and @Viicos in #13147
+ * Preserve `RootModel` core metadata by @Viicos in #13129
+ * Handle `AttributeError` subclasses with `from_attributes` by
+ @Viicos in #13096
+ * Fix `ValidationInfo.field_name` missing with
+ `model_validate_json()` by @Viicos in #13084
+
+-------------------------------------------------------------------
Old:
----
pydantic-2.13.1.tar.gz
New:
----
pydantic-2.13.4.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pydantic.spec ++++++
--- /var/tmp/diff_new_pack.AER21n/_old 2026-05-12 19:26:32.270762773 +0200
+++ /var/tmp/diff_new_pack.AER21n/_new 2026-05-12 19:26:32.274762938 +0200
@@ -27,7 +27,7 @@
%endif
%{?sle15_python_module_pythons}
Name: python-pydantic%{psuffix}
-Version: 2.13.1
+Version: 2.13.4
Release: 0
Summary: Data validation and settings management using python type
hinting
License: MIT
@@ -38,7 +38,7 @@
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module packaging}
BuildRequires: %{python_module pip}
-BuildRequires: %{python_module pydantic-core = 2.46.1}
+BuildRequires: %{python_module pydantic-core = 2.46.4}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
%if %{with test}
@@ -61,7 +61,7 @@
BuildRequires: %{python_module rich}
%endif
Requires: python-annotated-types >= 0.6.0
-Requires: python-pydantic-core = 2.46.1
+Requires: python-pydantic-core = 2.46.4
Requires: python-typing-extensions >= 4.14.1
Requires: python-typing-inspection >= 0.4.2
Recommends: python-email-validator >= 2.0
++++++ pydantic-2.13.1.tar.gz -> pydantic-2.13.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/.github/workflows/ci.yml
new/pydantic-2.13.4/.github/workflows/ci.yml
--- old/pydantic-2.13.1/.github/workflows/ci.yml 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/.github/workflows/ci.yml 2026-05-06
10:27:14.000000000 +0200
@@ -70,7 +70,7 @@
core-build:
name: core / Build on ${{ matrix.os }} (${{ matrix.target }} - ${{
matrix.interpreter || 'all' }}${{ matrix.os == 'linux' && format(' - {0}',
matrix.manylinux == 'auto' && 'manylinux' || matrix.manylinux) || '' }})
# only run on push to main, if a full build was requested or on release:
- if: startsWith(github.ref, 'refs/tags/') || github.ref ==
'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'full
build')
+ if: startsWith(github.ref, 'refs/tags/v') || github.ref ==
'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'full
build')
strategy:
fail-fast: false
matrix:
@@ -187,7 +187,7 @@
core-build-pgo:
name: core / Build PGO-optimized on ${{ matrix.platform.os }} / ${{
matrix.interpreter }}
# only run on push to main, if a full build was requested or on release:
- if: startsWith(github.ref, 'refs/tags/') || github.ref ==
'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'full
build')
+ if: startsWith(github.ref, 'refs/tags/v') || github.ref ==
'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'full
build')
strategy:
fail-fast: false
matrix:
@@ -525,8 +525,7 @@
matrix:
python-version:
- '3.13'
- # Segmentation fault on pypy3.11.15 (latest), see
https://github.com/pypy/pypy/issues/5400
- - 'pypy3.11.13'
+ - 'pypy3.11'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #
v6.0.2
@@ -560,7 +559,7 @@
build-pydantic:
runs-on: ubuntu-latest
- if: startsWith(github.ref, 'refs/tags/')
+ if: startsWith(github.ref, 'refs/tags/v')
outputs:
pydantic-version: ${{ steps.check-tag.outputs.VERSION }}
@@ -645,8 +644,7 @@
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
- # Segmentation fault on pypy3.11.15 (latest), see
https://github.com/pypy/pypy/issues/5400
- python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14',
'3.14t', 'pypy3.11.13']
+ python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14',
'3.14t', 'pypy3.11']
include:
# test macos intel just with latest python version
- os: macos-15-intel
@@ -884,7 +882,7 @@
release-pydantic-core:
needs: [core-test-builds-arch, core-test-builds-os, core-build-sdist,
check]
- if: needs.check.outputs.result == 'success' && startsWith(github.ref,
'refs/tags/')
+ if: needs.check.outputs.result == 'success' && startsWith(github.ref,
'refs/tags/v')
runs-on: ubuntu-latest
environment: release
@@ -943,7 +941,7 @@
release-pydantic:
needs: [build-pydantic, release-pydantic-core]
- if: startsWith(github.ref, 'refs/tags/')
+ if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
environment: release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/CITATION.cff
new/pydantic-2.13.4/CITATION.cff
--- old/pydantic-2.13.1/CITATION.cff 2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/CITATION.cff 2026-05-06 10:27:14.000000000 +0200
@@ -44,5 +44,5 @@
- hints
- typing
license: MIT
-version: v2.13.1
-date-released: 2026-04-15
+version: v2.13.4
+date-released: 2026-05-06
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/HISTORY.md
new/pydantic-2.13.4/HISTORY.md
--- old/pydantic-2.13.1/HISTORY.md 2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/HISTORY.md 2026-05-06 10:27:14.000000000 +0200
@@ -2,6 +2,41 @@
<!-- markdownlint-disable descriptive-link-text -->
<!-- markdownlint-disable-next-line first-line-heading -->
+## v2.13.4 (2026-05-06)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.4)
+
+### What's Changed
+
+#### Packaging
+
+* Bump libc from 0.2.155 to 0.2.185 by @Viicos in
[#13109](https://github.com/pydantic/pydantic/pull/13109)
+* Adapt `pydantic-core` linker flags on macOS by @washingtoneg and @Viicos in
[#13147](https://github.com/pydantic/pydantic/pull/13147)
+
+#### Fixes
+
+* Preserve `RootModel` core metadata by @Viicos in
[#13129](https://github.com/pydantic/pydantic/pull/13129)
+
+## v2.13.3 (2026-04-20)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.3)
+
+### What's Changed
+
+#### Fixes
+
+* Handle `AttributeError` subclasses with `from_attributes` by @Viicos in
[#13096](https://github.com/pydantic/pydantic/pull/13096)
+
+## v2.13.2 (2026-04-17)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.2)
+
+### What's Changed
+
+#### Fixes
+
+* Fix `ValidationInfo.field_name` missing with `model_validate_json()` by
@Viicos in [#13084](https://github.com/pydantic/pydantic/pull/13084)
+
## v2.13.1 (2026-04-15)
[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/docs/index.md
new/pydantic-2.13.4/docs/index.md
--- old/pydantic-2.13.1/docs/index.md 2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/docs/index.md 2026-05-06 10:27:14.000000000 +0200
@@ -14,67 +14,25 @@
Fast and extensible, Pydantic plays nicely with your linters/IDE/brain. Define
how data should be in pure, canonical Python 3.9+; validate it with Pydantic.
-!!! logfire "Monitor Pydantic with Pydantic Logfire :fire:"
- **[Pydantic Logfire](https://pydantic.dev/logfire)** is a production-grade
observability platform for AI and general applications. See LLM interactions,
agent behavior, API requests, and database queries in one unified trace. With
SDKs for Python, JavaScript/TypeScript, and Rust, Logfire works with all
OpenTelemetry-compatible languages.
+**Sign up for our newsletter, *The Pydantic Stack*, with updates & tutorials
on Pydantic, Logfire, and Pydantic AI:**
- Logfire integrates with many popular Python libraries including FastAPI,
OpenAI and Pydantic itself, so you can use Logfire to monitor Pydantic
validations and understand why some inputs fail validation:
-
- ```python {title="Monitoring Pydantic with Logfire" test="skip"}
- from datetime import datetime
-
- import logfire
-
- from pydantic import BaseModel
-
- logfire.configure()
- logfire.instrument_pydantic() # (1)!
-
-
- class Delivery(BaseModel):
- timestamp: datetime
- dimensions: tuple[int, int]
-
-
- # this will record details of a successful validation to logfire
- m = Delivery(timestamp='2020-01-02T03:04:05Z', dimensions=['10', '20'])
- print(repr(m.timestamp))
- #> datetime.datetime(2020, 1, 2, 3, 4, 5, tzinfo=TzInfo(UTC))
- print(m.dimensions)
- #> (10, 20)
-
- Delivery(timestamp='2020-01-02T03:04:05Z', dimensions=['10']) # (2)!
- ```
-
- 1. Set logfire record all both successful and failed validations, use
`record='failure'` to only record failed validations, [learn
more](https://logfire.pydantic.dev/docs/integrations/pydantic/).
- 2. This will raise a `ValidationError` since there are too few
`dimensions`, details of the input data and validation errors will be recorded
in Logfire.
-
- Would give you a view like this in the Logfire platform:
-
- [](https://logfire.pydantic.dev/docs/guides/web-ui/live/)
-
- This is just a toy example, but hopefully makes clear the potential value
of instrumenting a more complex application.
-
- **[Learn more about Pydantic Logfire](https://logfire.pydantic.dev/docs/)**
-
- **Sign up for our newsletter, *The Pydantic Stack*, with updates &
tutorials on Pydantic, Logfire, and Pydantic AI:**
-
- <form method="POST"
action="https://eu.customerioforms.com/forms/submit_action?site_id=53d2086c3c4214eaecaa&form_id=14b22611745b458&success_url=https://docs.pydantic.dev/"
class="md-typeset" style="display: flex; align-items: center; gap: 0.5rem;
max-width: 100%;">
- <input
- type="email"
- id="email_input"
- name="email"
- class="md-input md-input--stretch"
- style="flex: 1; background: var(--md-default-bg-color); color:
var(--md-default-fg-color);"
- required
- placeholder="Email"
- data-1p-ignore
- data-lpignore="true"
- data-protonpass-ignore="true"
- data-bwignore="true"
- />
- <input type="hidden" id="source_input" name="source"
value="pydantic" />
- <button type="submit" class="md-button
md-button--primary">Subscribe</button>
- </form>
+<form method="POST"
action="https://eu.customerioforms.com/forms/submit_action?site_id=53d2086c3c4214eaecaa&form_id=14b22611745b458&success_url=https://docs.pydantic.dev/"
class="md-typeset" style="display: flex; align-items: center; gap: 0.5rem;
max-width: 100%;">
+ <input
+ type="email"
+ id="email_input"
+ name="email"
+ class="md-input md-input--stretch"
+ style="flex: 1; background: var(--md-default-bg-color); color:
var(--md-default-fg-color);"
+ required
+ placeholder="Email"
+ data-1p-ignore
+ data-lpignore="true"
+ data-protonpass-ignore="true"
+ data-bwignore="true"
+ />
+ <input type="hidden" id="source_input" name="source" value="pydantic" />
+ <button type="submit" class="md-button
md-button--primary">Subscribe</button>
+</form>
## Why use Pydantic?
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/docs/version-policy.md
new/pydantic-2.13.4/docs/version-policy.md
--- old/pydantic-2.13.1/docs/version-policy.md 2026-04-15 15:55:16.000000000
+0200
+++ new/pydantic-2.13.4/docs/version-policy.md 2026-05-06 10:27:14.000000000
+0200
@@ -34,7 +34,16 @@
## Pydantic V3 and beyond
-We expect to make new major releases roughly once a year going forward,
although as mentioned above, any associated breaking changes should be trivial
to fix compared to the V1-to-V2 transition.
+Any associated breaking changes in the V3 release will be introduced with
care. This will include bug fixes that can't be solved in V2 without
introducing a change in behavior
+(e.g. [fixing configuration not following the
MRO](https://github.com/pydantic/pydantic/issues/9992)), or changes that don't
remove existing functionality or doesn't provide
+proper alternatives.
+
+In V3, it is expected to merged `pydantic-core` as an internal sub-module of
Pydantic, meaning it won't be published as a standalone library anymore.
+
+## Tagging conventions
+
+Pydantic uses Git tags to identify all published versions on PyPI (including
alpha and beta releases), prefixed with the `v` character. Since v2.13,
`pydantic-core`
+has been merged as a workspace member inside the `pydantic` repository, and
subsequent `pydantic-core` releases are tagged as `core-vx.y.z`.
## Experimental Features
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pydantic-2.13.1/pydantic/_internal/_generate_schema.py
new/pydantic-2.13.4/pydantic/_internal/_generate_schema.py
--- old/pydantic-2.13.1/pydantic/_internal/_generate_schema.py 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic/_internal/_generate_schema.py 2026-05-06
10:27:14.000000000 +0200
@@ -840,8 +840,17 @@
generic_origin: type[BaseModel] | None = getattr(cls,
'__pydantic_generic_metadata__', {}).get('origin')
if cls.__pydantic_root_model__:
- # FIXME: should the common field metadata be used here?
- inner_schema, _ = self._common_field_schema('root',
fields['root'], decorators)
+ inner_schema, metadata = self._common_field_schema('root',
fields['root'], decorators)
+ if cls.__doc__ and metadata.get('pydantic_js_updates',
{}).get('description'):
+ # This is a bit of a leaky abstraction, but as the
model docstring takes priority
+ # over the root field's description, we need to
override it here. This can't be done
+ # in the JSON Schema generation logic because the
metadata's `pydantic_js_updates` are
+ # applied last, and overrides any value previously set
(so the description set from the
+ # docstring in
`GenerateJsonSchema._update_class_schema()` is overridden):
+ update_core_metadata(
+ metadata, pydantic_js_updates={'description':
inspect.cleandoc(cls.__doc__)}
+ )
+
inner_schema = apply_model_validators(inner_schema,
model_validators, 'inner')
model_schema = core_schema.model_schema(
cls,
@@ -852,6 +861,7 @@
post_init=getattr(cls, '__pydantic_post_init__', None),
config=core_config,
ref=model_ref,
+ metadata=metadata,
)
else:
fields_schema: core_schema.CoreSchema =
core_schema.model_fields_schema(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/pydantic/json_schema.py
new/pydantic-2.13.4/pydantic/json_schema.py
--- old/pydantic-2.13.1/pydantic/json_schema.py 2026-04-15 15:55:16.000000000
+0200
+++ new/pydantic-2.13.4/pydantic/json_schema.py 2026-05-06 10:27:14.000000000
+0200
@@ -1636,7 +1636,6 @@
"""
from ._internal._dataclasses import is_stdlib_dataclass
from .main import BaseModel
- from .root_model import RootModel
if (config_title := config.get('title')) is not None:
json_schema.setdefault('title', config_title)
@@ -1664,8 +1663,6 @@
if docstring:
json_schema.setdefault('description', inspect.cleandoc(docstring))
- elif issubclass(cls, RootModel) and (root_description :=
cls.__pydantic_fields__['root'].description):
- json_schema.setdefault('description', root_description)
extra = config.get('extra')
if 'additionalProperties' not in json_schema: # This check is
particularly important for `typed_dict_schema()`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/pydantic/version.py
new/pydantic-2.13.4/pydantic/version.py
--- old/pydantic-2.13.1/pydantic/version.py 2026-04-15 15:55:16.000000000
+0200
+++ new/pydantic-2.13.4/pydantic/version.py 2026-05-06 10:27:14.000000000
+0200
@@ -8,7 +8,7 @@
__all__ = 'VERSION', 'version_info'
-VERSION = '2.13.1'
+VERSION = '2.13.4'
"""The version of Pydantic.
This version specifier is guaranteed to be compliant with the [specification],
@@ -19,7 +19,7 @@
"""
# Keep this in sync with the version constraint in the `pyproject.toml`
dependencies:
-_COMPATIBLE_PYDANTIC_CORE_VERSION = '2.46.1'
+_COMPATIBLE_PYDANTIC_CORE_VERSION = '2.46.4'
def version_short() -> str:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/pydantic-core/Cargo.lock
new/pydantic-2.13.4/pydantic-core/Cargo.lock
--- old/pydantic-2.13.1/pydantic-core/Cargo.lock 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic-core/Cargo.lock 2026-05-06
10:27:14.000000000 +0200
@@ -366,9 +366,9 @@
[[package]]
name = "libc"
-version = "0.2.155"
+version = "0.2.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
[[package]]
name = "litemap"
@@ -454,7 +454,7 @@
[[package]]
name = "pydantic-core"
-version = "2.46.1"
+version = "2.46.4"
dependencies = [
"ahash",
"base64",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/pydantic-core/Cargo.toml
new/pydantic-2.13.4/pydantic-core/Cargo.toml
--- old/pydantic-2.13.1/pydantic-core/Cargo.toml 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic-core/Cargo.toml 2026-05-06
10:27:14.000000000 +0200
@@ -1,6 +1,6 @@
[package]
name = "pydantic-core"
-version = "2.46.1"
+version = "2.46.4"
edition = "2024"
license = "MIT"
homepage = "https://github.com/pydantic/pydantic"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/pydantic-core/build.rs
new/pydantic-2.13.4/pydantic-core/build.rs
--- old/pydantic-2.13.1/pydantic-core/build.rs 2026-04-15 15:55:16.000000000
+0200
+++ new/pydantic-2.13.4/pydantic-core/build.rs 2026-05-06 10:27:14.000000000
+0200
@@ -13,4 +13,13 @@
}
println!("cargo:rustc-check-cfg=cfg(specified_profile_use)");
println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap());
+
+ // The macOS `ld_prime` new Linker (used in macOS 15 GitHub CI runners),
+ // reserves significantly less Mach-O header padding than the old linker.
+ // Tools like Homebrew use install_name_tool to rewrite dylib paths
post-install,
+ // and that requires spare space in the header. Without this flag the
relink
+ // fails with "updated load commands do not fit in the header".
+ if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
+ println!("cargo:rustc-link-arg=-Wl,-headerpad_max_install_names");
+ }
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/pydantic-core/src/lookup_key.rs
new/pydantic-2.13.4/pydantic-core/src/lookup_key.rs
--- old/pydantic-2.13.1/pydantic-core/src/lookup_key.rs 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic-core/src/lookup_key.rs 2026-05-06
10:27:14.000000000 +0200
@@ -3,7 +3,7 @@
use std::fmt;
use pyo3::IntoPyObjectExt;
-use pyo3::exceptions::{PyTypeError, PyValueError};
+use pyo3::exceptions::{PyAttributeError, PyTypeError, PyValueError};
use pyo3::prelude::*;
use pyo3::pybacked::PyBackedStr;
use pyo3::types::{PyDict, PyList, PyMapping, PyString};
@@ -111,7 +111,7 @@
}
pub fn simple_py_get_attr<'py>(&self, obj: &Bound<'py, PyAny>) ->
PyResult<Option<Bound<'py, PyAny>>> {
- self.get_impl(obj, PyAnyMethods::getattr_opt, |d, loc|
loc.py_get_attrs(&d))
+ self.get_impl(obj, py_get_attrs, |d, loc| loc.py_get_attrs(&d))
}
pub fn py_get_attr<'py>(
@@ -366,7 +366,7 @@
// FIXME: should this instance check be for Mapping instead of
Dict, and use `mapping_get`?
Ok(obj.get_item(self).ok())
} else {
- obj.getattr_opt(self)
+ py_get_attrs(obj, self)
}
}
}
@@ -454,3 +454,20 @@
(self as u8 & other as u8) != 0
}
}
+
+// TODO replace with `PyAnyMethods::getattr_opt` once
https://github.com/PyO3/pyo3/pull/5985 is merged:
+fn py_get_attrs<'py, N>(obj: &Bound<'py, PyAny>, attr_name: N) ->
PyResult<Option<Bound<'py, PyAny>>>
+where
+ N: IntoPyObject<'py, Target = PyString>,
+{
+ match obj.getattr(attr_name) {
+ Ok(attr) => Ok(Some(attr)),
+ Err(err) => {
+ if err.get_type(obj.py()).is_subclass_of::<PyAttributeError>()? {
+ Ok(None)
+ } else {
+ Err(err)
+ }
+ }
+ }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pydantic-2.13.1/pydantic-core/src/validators/model_fields.rs
new/pydantic-2.13.4/pydantic-core/src/validators/model_fields.rs
--- old/pydantic-2.13.1/pydantic-core/src/validators/model_fields.rs
2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic-core/src/validators/model_fields.rs
2026-05-06 10:27:14.000000000 +0200
@@ -629,6 +629,8 @@
// dict, and try to set defaults for any missing fields
for (field, field_result) in std::iter::zip(&self.fields,
field_results) {
+ let state = &mut
state.scoped_set_field_name(Some(field.name.as_py_str().bind(py).clone()));
+
let field_value = if let Some((field_info, field_json_value)) =
field_result {
match field.validator.validate(py, field_json_value, state) {
Ok(value) => {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pydantic-2.13.1/pydantic-core/tests/validators/test_arguments.py
new/pydantic-2.13.4/pydantic-core/tests/validators/test_arguments.py
--- old/pydantic-2.13.1/pydantic-core/tests/validators/test_arguments.py
2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic-core/tests/validators/test_arguments.py
2026-05-06 10:27:14.000000000 +0200
@@ -1,5 +1,4 @@
import os
-import platform
import re
import sys
from functools import wraps
@@ -1139,9 +1138,6 @@
)
[email protected](
- platform.python_implementation() == 'PyPy' and sys.version_info[:2] == (3,
11), reason='pypy 3.11 type formatting'
-)
def test_error_display(pydantic_version):
v = SchemaValidator(
core_schema.arguments_schema(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pydantic-2.13.1/pydantic-core/tests/validators/test_definitions_recursive.py
new/pydantic-2.13.4/pydantic-core/tests/validators/test_definitions_recursive.py
---
old/pydantic-2.13.1/pydantic-core/tests/validators/test_definitions_recursive.py
2026-04-15 15:55:16.000000000 +0200
+++
new/pydantic-2.13.4/pydantic-core/tests/validators/test_definitions_recursive.py
2026-05-06 10:27:14.000000000 +0200
@@ -1,6 +1,5 @@
import datetime
import platform
-import sys
from dataclasses import dataclass
from typing import Optional
@@ -753,9 +752,6 @@
assert v.validate_python(long_input) == long_input
[email protected](
- platform.python_implementation() == 'PyPy' and sys.version_info[:2] == (3,
11), reason='pypy 3.11 type formatting'
-)
def test_error_inside_definition_wrapper():
with pytest.raises(SchemaError) as exc_info:
SchemaValidator(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pydantic-2.13.1/pydantic-core/tests/validators/test_string.py
new/pydantic-2.13.4/pydantic-core/tests/validators/test_string.py
--- old/pydantic-2.13.1/pydantic-core/tests/validators/test_string.py
2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic-core/tests/validators/test_string.py
2026-05-06 10:27:14.000000000 +0200
@@ -348,9 +348,6 @@
@pytest.mark.parametrize('mode', (None, 'schema', 'config'))
[email protected](
- platform.python_implementation() == 'PyPy' and sys.version_info[:2] == (3,
11), reason='pypy 3.11 type formatting'
-)
def test_backtracking_regex_rust_unsupported(mode) -> None:
pattern = r'r(#*)".*?"\1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pydantic-2.13.1/pydantic-core/tests/validators/test_union.py
new/pydantic-2.13.4/pydantic-core/tests/validators/test_union.py
--- old/pydantic-2.13.1/pydantic-core/tests/validators/test_union.py
2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pydantic-core/tests/validators/test_union.py
2026-05-06 10:27:14.000000000 +0200
@@ -1,5 +1,3 @@
-import platform
-import sys
from dataclasses import dataclass
from datetime import date, time
from enum import Enum, IntEnum
@@ -233,9 +231,6 @@
]
[email protected](
- platform.python_implementation() == 'PyPy' and sys.version_info[:2] == (3,
11), reason='pypy 3.11 type formatting'
-)
def test_empty_choices():
msg = r'Error building "union" validator:\s+SchemaError: One or more union
choices required'
with pytest.raises(SchemaError, match=msg):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/pyproject.toml
new/pydantic-2.13.4/pyproject.toml
--- old/pydantic-2.13.1/pyproject.toml 2026-04-15 15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/pyproject.toml 2026-05-06 10:27:14.000000000 +0200
@@ -47,7 +47,7 @@
'typing-extensions>=4.14.1',
'annotated-types>=0.6.0',
# Keep this in sync with the version in the
`check_pydantic_core_version()` function:
- 'pydantic-core==2.46.1',
+ 'pydantic-core==2.46.4',
'typing-inspection>=0.4.2',
]
dynamic = ['version', 'readme']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/tests/test_json_schema.py
new/pydantic-2.13.4/tests/test_json_schema.py
--- old/pydantic-2.13.1/tests/test_json_schema.py 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/tests/test_json_schema.py 2026-05-06
10:27:14.000000000 +0200
@@ -5391,6 +5391,38 @@
}
+def test_root_model_annotated_root_type_parameterized() -> None:
+ """https://github.com/pydantic/pydantic/issues/13123"""
+
+ MyType = Annotated[str, Field(examples=['hello'], description='desc',
deprecated=True)]
+
+ class MyModel(RootModel[MyType]):
+ pass
+
+ assert MyModel.model_json_schema() == {
+ 'deprecated': True,
+ 'description': 'desc',
+ 'examples': ['hello'],
+ 'title': 'MyModel',
+ 'type': 'string',
+ }
+
+
+def test_root_model_annotated_root_type() -> None:
+ """https://github.com/pydantic/pydantic/issues/13123"""
+
+ class MyModel(RootModel):
+ root: Annotated[str, Field(examples=['hello'], description='desc',
deprecated=True)]
+
+ assert MyModel.model_json_schema() == {
+ 'deprecated': True,
+ 'description': 'desc',
+ 'examples': ['hello'],
+ 'title': 'MyModel',
+ 'type': 'string',
+ }
+
+
def test_type_adapter_json_schemas_title_description():
class Model(BaseModel):
a: str
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/tests/test_main.py
new/pydantic-2.13.4/tests/test_main.py
--- old/pydantic-2.13.1/tests/test_main.py 2026-04-15 15:55:16.000000000
+0200
+++ new/pydantic-2.13.4/tests/test_main.py 2026-05-06 10:27:14.000000000
+0200
@@ -2010,11 +2010,7 @@
def test_class_kwargs_custom_config():
- if platform.python_implementation() == 'PyPy':
- msg = r"__init_subclass__\(\) got an unexpected keyword argument
'some_config'"
- else:
- msg = r'__init_subclass__\(\) takes no keyword arguments'
- with pytest.raises(TypeError, match=msg):
+ with pytest.raises(TypeError, match=r'__init_subclass__\(\) takes no
keyword arguments'):
class Model(BaseModel, some_config='new_value'):
a: int
@@ -3056,6 +3052,25 @@
assert res == ModelFromAttributesFalse(x=1)
+def test_from_attributes_attributeerror_subclass() -> None:
+ """https://github.com/pydantic/pydantic/issues/13092"""
+
+ class SubAttributeError(AttributeError):
+ pass
+
+ class Model(BaseModel, from_attributes=True):
+ field: Union[int, None] = None
+
+ class Obj:
+ @property
+ def child(self):
+ raise SubAttributeError()
+
+ m = Model.model_validate(Obj())
+
+ assert m.field is None
+
+
@pytest.mark.parametrize(
'field_type,input_value,expected,raises_match,strict',
[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/tests/test_missing_sentinel.py
new/pydantic-2.13.4/tests/test_missing_sentinel.py
--- old/pydantic-2.13.1/tests/test_missing_sentinel.py 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/tests/test_missing_sentinel.py 2026-05-06
10:27:14.000000000 +0200
@@ -2,6 +2,7 @@
from typing import Annotated, Union
import pytest
+import typing_extensions
from annotated_types import Ge
from pydantic_core import MISSING, PydanticSerializationUnexpectedValue
@@ -53,7 +54,11 @@
f: Union[int, MISSING] = MISSING
[email protected](reason="PEP 661 sentinels aren't picklable yet in the
experimental typing-extensions implementation")
[email protected](
+ # Unreleased typing-extensions has the final sentinel implementation with
pickle support:
+ condition=not hasattr(typing_extensions, 'sentinel'),
+ reason="PEP 661 sentinels aren't picklable yet in the experimental
typing-extensions implementation",
+)
def test_missing_sentinel_pickle() -> None:
m = ModelPickle()
m_reconstructed = pickle.loads(pickle.dumps(m))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/tests/test_pickle.py
new/pydantic-2.13.4/tests/test_pickle.py
--- old/pydantic-2.13.1/tests/test_pickle.py 2026-04-15 15:55:16.000000000
+0200
+++ new/pydantic-2.13.4/tests/test_pickle.py 2026-05-06 10:27:14.000000000
+0200
@@ -6,7 +6,7 @@
import sys
from pathlib import Path
from textwrap import dedent
-from typing import Optional
+from typing import TYPE_CHECKING, Optional
import pytest
@@ -15,17 +15,31 @@
from pydantic._internal._model_construction import _PydanticWeakRef
from pydantic.config import ConfigDict
-try:
+IS_PYPY = sys.implementation.name == 'pypy' and sys.version_info >= (3, 11)
+
+if TYPE_CHECKING:
import cloudpickle
-except ImportError:
- cloudpickle = None
+else:
+ if not IS_PYPY:
+ try:
+ import cloudpickle
+ except ImportError:
+ cloudpickle = None
+ else:
+ cloudpickle = None
TEST_DATA_DIR = Path(__file__).parent / 'test_data'
-pytestmark = pytest.mark.skipif(cloudpickle is None, reason='cloudpickle is
not installed')
+pytestmark = pytest.mark.skipif(
+ cloudpickle is None,
+ reason='cloudpickle is not installed, or tests are running with PyPy
(https://github.com/cloudpipe/cloudpickle/issues/592).',
+)
+# Note: this xfail marker was used when cloudpickle was partially compatible
with PyPy. Since PyPy 7.3.22, it isn't compatible
+# at all (importing it fails), so all tests are skipped as per the module's
`pytestmark`. We keep the xfail marker if this ever
+# changes:
cloudpickle_pypy_xfail = pytest.mark.xfail(
- condition=sys.implementation.name == 'pypy' and sys.version_info >= (3,
11),
+ condition=IS_PYPY,
reason='Cloudpickle issue: - possibly
https://github.com/cloudpipe/cloudpickle/issues/557',
)
@@ -99,7 +113,10 @@
# Importable model can be pickled with either pickle or cloudpickle.
(ImportableModel, False),
(ImportableModel, True),
- # Locally-defined model can only be pickled with cloudpickle.
+ # Locally-defined model can only be pickle
+ # # Note: this xfail marker was used when cloudpickle was partially
compatible with PyPy. Since PyPy 7.3.22, it is completelyisn't compatible
+ # # at all (importing it fails), so all tests are skipped as per the
module's `pytestmark`. We keep the xfail marker if this ever
+ # # changes:d with cloudpickle.
pytest.param(model_factory(), True, marks=cloudpickle_pypy_xfail),
],
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/tests/test_validators.py
new/pydantic-2.13.4/tests/test_validators.py
--- old/pydantic-2.13.1/tests/test_validators.py 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/tests/test_validators.py 2026-05-06
10:27:14.000000000 +0200
@@ -3145,3 +3145,45 @@
) # Create a Sub instance without triggering validation (e.g., using
model_construct)
# Attempt to create Base with the Sub instance. This line should succeed
if the bug is fixed, but currently raises ValidationError.
Base(sub=sub) # <-- This throws AssertionError because Sub's 'after'
validator runs again.
+
+
+def test_model_validate_by_json_field_validator_with_validation_info() -> None:
+ """https://github.com/pydantic/pydantic/issues/13074"""
+
+ class Foo(BaseModel):
+ field1: int
+ field2: int
+
+ @field_validator('field2')
+ @classmethod
+ def _validate_field2(cls, v: int, info: ValidationInfo) -> int:
+ assert info.field_name in ('field1', 'field2')
+ assert info.context == 'context'
+
+ return v + info.data['field1']
+
+ f1 = Foo.model_validate({'field1': 1, 'field2': 2}, context='context')
+ f2 = Foo.model_validate_json('{"field1": 1, "field2": 2}',
context='context')
+
+ assert f1.field1 == f2.field1 == 1
+ assert f1.field2 == f2.field2 == 3
+
+
+def test_model_validate_json_default_value_validator_with_validation_info() ->
None:
+ """https://github.com/pydantic/pydantic/issues/13074"""
+
+ class Foo(BaseModel, validate_default=True):
+ field: int = 1
+
+ @field_validator('field')
+ @classmethod
+ def _validate_field(cls, v: int, info: ValidationInfo) -> int:
+ assert info.field_name == 'field'
+ assert info.context == 'context'
+
+ return v + 1
+
+ f1 = Foo.model_validate({'field': 1}, context='context')
+ f2 = Foo.model_validate_json('{"field1": 1}', context='context')
+
+ assert f1.field == f2.field == 2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pydantic-2.13.1/tests/types/test_model.py
new/pydantic-2.13.4/tests/types/test_model.py
--- old/pydantic-2.13.1/tests/types/test_model.py 2026-04-15
15:55:16.000000000 +0200
+++ new/pydantic-2.13.4/tests/types/test_model.py 2026-05-06
10:27:14.000000000 +0200
@@ -7,8 +7,6 @@
ConfigDict,
SerializationInfo,
TypeAdapter,
- ValidationInfo,
- field_validator,
model_serializer,
)
@@ -85,22 +83,3 @@
else:
assert serializer.to_python(ModelB(a=123, b='test'), **kwargs) ==
'ModelA'
assert serializer.to_json(ModelB(a=123, b='test'), **kwargs) ==
b'"ModelA"'
-
-
-def test_model_validate_by_json_with_validation_info_data():
- """https://github.com/pydantic/pydantic/issues/13074"""
-
- class Foo(BaseModel):
- field1: int
- field2: int
-
- @field_validator('field2')
- @classmethod
- def _validate_field2(cls, v: str, info: ValidationInfo) -> int:
- return v + info.data['field1']
-
- f1 = Foo.model_validate({'field1': 1, 'field2': 2})
- f2 = Foo.model_validate_json('{"field1": 1, "field2": 2}')
-
- assert f1.field1 == f2.field1 == 1
- assert f1.field2 == f2.field2 == 3