Skip to content

Fix pickling of exception classes with kw_only attributes (#734)#1534

Closed
r266-tech wants to merge 2 commits intopython-attrs:mainfrom
r266-tech:fix/kw-only-exc-pickle
Closed

Fix pickling of exception classes with kw_only attributes (#734)#1534
r266-tech wants to merge 2 commits intopython-attrs:mainfrom
r266-tech:fix/kw-only-exc-pickle

Conversation

@r266-tech
Copy link
Copy Markdown

Problem

Exception classes decorated with auto_exc=True and kw_only=True cannot be pickled. BaseException.__reduce__ returns (cls, self.args, state), passing self.args as positional arguments to cls() on unpickle. When kw_only=True, __init__ only accepts keyword-only arguments, causing:

TypeError: __init__() takes 1 positional argument but 2 were given

Reproducer:

import attr, pickle

@attr.s(slots=False, auto_exc=True, kw_only=True)
class KwOnly(Exception):
    field: int = attr.ib()

pickle.loads(pickle.dumps(KwOnly(field=1)))  # TypeError

Fix

Added a custom __reduce__ for exception classes that have any kw_only attrs. It uses a _rebuild_exc helper that:

  1. Creates the instance via cls.__new__(cls) — bypassing __init__
  2. Restores attributes directly via object.__setattr__
  3. Calls BaseException.__init__ to properly restore self.args

The fix is minimal (only applied when the class is an exception AND has kw_only attrs) and does not affect non-exception classes or exceptions without kw_only.

Tests

Added test_auto_exc_kw_only_pickle (parametrized over slots, frozen, pickle protocol) and test_auto_exc_mixed_kw_only_pickle (mixed positional + kw_only attrs).

Closes #734

contributor and others added 2 commits March 23, 2026 03:13
BaseException.__reduce__ returns (cls, self.args, state), passing
self.args as positional arguments to cls() on unpickle. When kw_only=True,
__init__ only accepts keyword-only arguments, causing unpickling to fail
with: TypeError: __init__() takes 1 positional argument but 2 were given

This fix adds a custom __reduce__ for exception classes that have
kw_only attrs. It uses a _rebuild_exc helper that creates the instance
via __new__ (bypassing __init__) and restores attributes directly,
then calls BaseException.__init__ to properly set self.args.

Fixes GH#734
@hynek
Copy link
Copy Markdown
Member

hynek commented Mar 24, 2026

AI slop

@hynek hynek closed this Mar 24, 2026
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.

pickling of Exceptions and kw_only

3 participants