Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Structure hook on a PEP 695 alias is ignored inside a passthrough union

Đang mở
#797 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@Eryx-Z đang làm issue này rồi.

Từ ngày 16/10/2026.

  • #799 của @Eryx-Z — đang mở

Đánh giá

Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
62/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
data

Hướng nghiên cứu

Start with the reproduction script in the issue, then read get_passthrough_base in src/cattrs/strategies/_unions.py (around lines 180-183 and 202-207), where a PEP 695 alias is unwrapped to its value before the union hook is built. The preconf converters pick this up through src/cattrs/preconf/json.py. Done means structure(' x ', Trimmed | int) returns 'x' again, matching 26.1.0, with a regression test that registers a hook on an alias and structures a union containing it.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Since 26.2.0, a structure hook registered for a PEP 695 type alias is skipped when the alias is a member of a union handled by the union passthrough strategy, which every preconf converter configures, so the raw value comes back with no error.

Minimal example

import cattrs
from cattrs.preconf.json import make_converter
from cattrs.strategies import configure_union_passthrough

type Trimmed = str


def build_plain():
    c = cattrs.Converter()
    configure_union_passthrough(str | int, c)
    return c


for name, c in (("Converter + passthrough", build_plain()), ("preconf.json", make_converter())):
    c.register_structure_hook_func(lambda t: t is Trimmed, lambda v, _: v.strip())
    print(f"[{name}]")
    print("  structure('  x  ', Trimmed)       observed:", repr(c.structure("  x  ", Trimmed)))
    print("  structure('  x  ', Trimmed | int) observed:", repr(c.structure("  x  ", Trimmed | int)))
    print("  structure('  x  ', Trimmed | int) expected:", repr("x"))

Observed output

[Converter + passthrough]
  structure('  x  ', Trimmed)       observed: 'x'
  structure('  x  ', Trimmed | int) observed: '  x  '
  structure('  x  ', Trimmed | int) expected: 'x'
[preconf.json]
  structure('  x  ', Trimmed)       observed: 'x'
  structure('  x  ', Trimmed | int) observed: '  x  '
  structure('  x  ', Trimmed | int) expected: 'x'

The hook runs for Trimmed on its own but not for Trimmed | int. Trimmed | int | None behaves the same way.

Expected behaviour

'x' in both cases, as 26.1.0 returns for the same script. The Type Aliases section of docs/defaulthooks.md says alias hooks can be overridden with predicate hooks, and its versionchanged 26.2.0 note about the passthrough strategy doesn't say those hooks are skipped inside unions.

Versions

  • Python 3.12.3
  • cattrs 26.2.1 (26.1.0 is not affected)
  • attrs 26.1.0
  • OS: Linux

Cause

In configure_union_passthrough (src/cattrs/strategies/_unions.py, main at 73fa273), get_passthrough_base (lines 180-181) replaces an alias with its value, so Trimmed becomes str in args (line 183) and in non_literal_classes (lines 202-207). The alias is then not in spillover, and the native union hook returns the value as is because val.__class__ in classes (lines 251 and 262). Before 26.2.0 the alias was not unwrapped, went to spillover, and converter.structure(val, Trimmed) reached the user hook. cattrs.preconf.json.make_converter() configures the passthrough at src/cattrs/preconf/json.py line 64, and the other preconf converters do the same, so any hook on a str, int, float, bool or bytes alias used in such a union is affected.

Proposed fix

Unwrap an alias only when the converter has no dedicated hook for it, for example when converter.get_structure_hook(alias) is still the product of the default type alias factory; otherwise leave the alias in spillover while the union is still claimed through its other members. A test that registers a hook on an alias and structures Alias | int would cover it.

I'm happy to open a PR with this fix and a test.

Ngôn ngữ chính
Python
Star
1.1k
Fork
171
Merge trung bình
14 giờ 46 phút
Pull request đã merge (30 ngày)
5

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của python-attrs/cattrs

Tất cả issue của python-attrs/cattrs

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.