Skip to content

fix: use subclass check for AttributeError in getattr_opt on Python < 3.13#5985

Merged
davidhewitt merged 4 commits intoPyO3:mainfrom
Harikeshav-R:fix-getattr-opt-subclass-check
Apr 24, 2026
Merged

fix: use subclass check for AttributeError in getattr_opt on Python < 3.13#5985
davidhewitt merged 4 commits intoPyO3:mainfrom
Harikeshav-R:fix-getattr-opt-subclass-check

Conversation

@Harikeshav-R
Copy link
Copy Markdown
Contributor

The #[cfg(not(Py_3_13))] fallback path of getattr_opt used .is() (type identity) to check whether an error was AttributeError. This misses AttributeError subclasses — they get propagated as errors instead of being treated as "attribute not found".

On Python 3.13+, PyObject_GetOptionalAttr internally uses PyErr_ExceptionMatches which does a proper subclass check, so this inconsistency only affects Python < 3.13.

This PR replaces .is() with .is_subclass_of::<PyAttributeError>() to match the 3.13+ semantics, and adds a test covering the case.

Closes #5984

Discovered via pydantic/pydantic#13092 — pydantic's from_attributes=True regressed on Python 3.12 after switching from a hand-rolled getattr wrapper (which did subclass checking) to PyO3's getattr_opt.

… 3.13

The `#[cfg(not(Py_3_13))]` branch of `getattr_opt` used `.is()` (type
identity) to check for `AttributeError`, which missed subclasses. This
replaces it with `.is_subclass_of::<PyAttributeError>()` to match the
behavior of `PyObject_GetOptionalAttr` on Python 3.13+.
Copilot AI review requested due to automatic review settings April 18, 2026 03:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes getattr_opt behavior on Python < 3.13 to treat AttributeError subclasses as “attribute not found”, aligning with Python 3.13+ semantics, and adds a regression test.

Changes:

  • Use subclass checking instead of type identity when determining whether to swallow an AttributeError in getattr_opt (Python < 3.13 path).
  • Add a unit test that raises an AttributeError subclass from a property and asserts getattr_opt returns None.
  • Add a newsfragment documenting the fix.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/types/any.rs Updates error-type checking in getattr_opt and adds a regression test for AttributeError subclasses.
newsfragments/5985.fixed.md Documents the behavior fix for Python < 3.13.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/types/any.rs Outdated
Switch from `err.get_type().is_subclass_of::<PyAttributeError>()?` to
`err.is_instance_of::<PyAttributeError>()` which is infallible and
avoids masking the original error if the subclass check itself fails.

This also matches the pattern used by `hasattr` in the same file.
Comment thread src/types/any.rs Outdated
…ions

Add a compatibility shim for PyObject_GetOptionalAttr in
pyo3-ffi/src/compat/py_3_13.rs that uses PyObject_GetAttr +
PyErr_ExceptionMatches on Python < 3.13, matching the real C API
behavior on 3.13+.

Simplify getattr_opt to use the compat function unconditionally,
removing the #[cfg] branching.
@davidhewitt davidhewitt added this pull request to the merge queue Apr 23, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 23, 2026
@davidhewitt davidhewitt added this pull request to the merge queue Apr 24, 2026
Merged via the queue into PyO3:main with commit eeb3214 Apr 24, 2026
44 of 45 checks passed
@Harikeshav-R Harikeshav-R deleted the fix-getattr-opt-subclass-check branch April 24, 2026 14:06
davidhewitt pushed a commit to davidhewitt/pyo3 that referenced this pull request May 1, 2026
… 3.13 (PyO3#5985)

* fix: use subclass check for AttributeError in getattr_opt on Python < 3.13

The `#[cfg(not(Py_3_13))]` branch of `getattr_opt` used `.is()` (type
identity) to check for `AttributeError`, which missed subclasses. This
replaces it with `.is_subclass_of::<PyAttributeError>()` to match the
behavior of `PyObject_GetOptionalAttr` on Python 3.13+.

* chore: rename newsfragment to PR PyO3#5985

* fix: use infallible is_instance_of instead of fallible is_subclass_of

Switch from `err.get_type().is_subclass_of::<PyAttributeError>()?` to
`err.is_instance_of::<PyAttributeError>()` which is infallible and
avoids masking the original error if the subclass check itself fails.

This also matches the pattern used by `hasattr` in the same file.

* fix: add PyObject_GetOptionalAttr compat shim and use it for all versions

Add a compatibility shim for PyObject_GetOptionalAttr in
pyo3-ffi/src/compat/py_3_13.rs that uses PyObject_GetAttr +
PyErr_ExceptionMatches on Python < 3.13, matching the real C API
behavior on 3.13+.

Simplify getattr_opt to use the compat function unconditionally,
removing the #[cfg] branching.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

getattr_opt uses identity check instead of subclass check for AttributeError on Python < 3.13

3 participants