Skip to content

Commit

Permalink
Fix crash when __pre_init__, kw_only, and defaults come together (#1319)
Browse files Browse the repository at this point in the history
* Fix crash when __pre_init__, kw_only, and defaults come together

Fixes #1284

* Add news fragment

* Improve test
  • Loading branch information
hynek authored Aug 3, 2024
1 parent 689a0e6 commit 09161fc
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog.d/1319.change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The combination of a `__attrs_pre_init__` that takes arguments, a kw-only field, and a default on that field does not crash anymore.
12 changes: 7 additions & 5 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -2207,15 +2207,17 @@ def _attrs_to_init_script(
# leading comma & kw_only args
args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}"
pre_init_kw_only_args = ", ".join(
[f"{kw_arg}={kw_arg}" for kw_arg in kw_only_args]
[
f"{kw_arg_name}={kw_arg_name}"
# We need to remove the defaults from the kw_only_args.
for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args)
]
)
pre_init_args += (
", " if pre_init_args else ""
) # handle only kwargs and no regular args
pre_init_args += ", " if pre_init_args else ""
pre_init_args += pre_init_kw_only_args

if call_pre_init and pre_init_has_args:
# If pre init method has arguments, pass same arguments as `__init__`
# If pre init method has arguments, pass same arguments as `__init__`.
lines[0] = f"self.__attrs_pre_init__({pre_init_args})"

# Python 3.7 doesn't allow backslashes in f strings.
Expand Down
19 changes: 19 additions & 0 deletions tests/test_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,25 @@ def __attrs_pre_init__(self2, y):

assert 12 == getattr(c, "z", None)

@pytest.mark.usefixtures("with_and_without_validation")
def test_pre_init_kw_only_work_with_defaults(self):
"""
Default values together with kw_only don't break __attrs__pre_init__.
"""
val = None

@attr.define
class KWOnlyAndDefault:
kw_and_default: int = attr.field(kw_only=True, default=3)

def __attrs_pre_init__(self, *, kw_and_default):
nonlocal val
val = kw_and_default

inst = KWOnlyAndDefault()

assert 3 == val == inst.kw_and_default

@pytest.mark.usefixtures("with_and_without_validation")
def test_post_init(self):
"""
Expand Down

0 comments on commit 09161fc

Please sign in to comment.