[Vấn đề Python 3.10] Thay đổi nội bộ làm lộ lỗi (PEP 626 & các thành phần liên quan)
By JoeVu, at: 15:01 Ngày 06 tháng 8 năm 2025
Thời gian đọc ước tính: __READING_TIME__ phút
![[Python 3.10 issues] Internal Changes Exposed Bugs (PEP 626 & friends)](/media/filer_public_thumbnails/filer_public/1c/ce/1cce4ecb-573f-4c12-b812-74284cc12ee3/python_310_issues_internal_changes_exposed_bugs_pep_626__friends.png__1500x900_q85_crop_subsampling-2_upscale.jpg)
![[Python 3.10 issues] Internal Changes Exposed Bugs (PEP 626 & friends)](/media/filer_public_thumbnails/filer_public/1c/ce/1cce4ecb-573f-4c12-b812-74284cc12ee3/python_310_issues_internal_changes_exposed_bugs_pep_626__friends.png__400x240_q85_crop_subsampling-2_upscale.jpg)
Các triệu chứng bạn có thể thấy
-
Debugger/profiler/tracer hoạt động bất thường (điểm ngắt bỏ qua dòng, độ phủ không khớp).
-
Các công cụ dựa trên offset bytecode bị hỏng (ví dụ: giả định về frame.f_lasti).
-
Công cụ đo lường tùy chỉnh kiểm tra frame/bytecode trở nên không chính xác.
Tại sao điều này xảy ra
Python 3.10 đã áp dụng bảng số dòng chính xác (PEP 626), thay đổi cách trình thông dịch ánh xạ bytecode vào các dòng nguồn. Là một phần của điều này:
-
frame.f_lasti về cơ bản trở thành một offset hướng dẫn thay vì một offset byte thô (trình thông dịch đã chuyển sang wordcode trước đây; 3.10 đã thắt chặt ngữ nghĩa). Xem Những gì mới trong 3.10 → Tracebacks & số dòng và các tài liệu về đối tượng frame.
-
Bảng số dòng giờ đây chính xác hơn đối với ngoại lệ và truy tìm, điều này rất tốt cho người dùng, nhưng làm hỏng các công cụ phụ thuộc vào các hành vi cũ, không chính thức.
Sửa chữa / mô hình di chuyển
1) Không dựa vào các chi tiết bytecode nội bộ. Sử dụng API được hỗ trợ.
-
Ưu tiên mô-đun dis để lập luận về các hướng dẫn/offset thay vì mã hóa cứng offset.
-
Đối với vị trí nguồn, hãy dựa vào frame.f_lineno và các thuộc tính của đối tượng code (co_filename, co_firstlineno, co_positions() trong 3.11+) thay vì tự giải mã nội bộ. Xem inspect.
2) Cập nhật logic truy tìm/gỡ lỗi cho ngữ nghĩa PEP 626.
-
Nếu bạn thực hiện sys.settrace/sys.setprofile, hãy mong đợi nhiều sự kiện truy tìm hơn và các bước nhảy dòng chính xác hơn. Xem lại các tài liệu về móc truy tìm và điều chỉnh bộ lọc để bạn không tính quá mức độ phủ hoặc bỏ lỡ các nhánh.
3) Sử dụng các thư viện cấp cao hơn nếu có thể.
-
Đối với việc viết lại/phân tích mã, hãy ưu tiên ast/LibCST hơn là bytecode poking. Bytecode không phải là giao diện ổn định; AST thì có.
4) Xây dựng lại & kiểm tra lại các tiện ích mở rộng C và bất kỳ tích hợp frame/bytecode nào.
-
Nếu tiện ích mở rộng C chạm vào nội bộ frame hoặc đối tượng code, hãy biên dịch lại trên 3.10 và kiểm tra lại so với các thay đổi C-API 3.10.
Ví dụ: công cụ truy tìm giả sử offset byte
# Cũ: so sánh f_lasti thô (được coi như offset byte)
def trace(frame, event, arg):
if frame.f_lasti in SOME_BYTE_OFFSETS: # dễ gãy
...
return trace
# Mới: sử dụng dis để ánh xạ offset hướng dẫn một cách mạnh mẽ
import dis
def trace(frame, event, arg):
code = frame.f_code
instrs = list(dis.get_instructions(code))
# khớp bằng (offset, starts_line) hoặc bằng mẫu opname/name thay vì số thô
...
return trace
Cách tránh đau khổ lần sau
-
Coi bytecode là không ổn định. Nếu bạn phải kiểm tra nó, hãy sử dụng dis và giữ cho logic có khả năng chống lại sự thay đổi offset.
-
Thêm CI trên các phiên bản Python mới sớm (alpha/beta) để các công cụ truy tìm/độ phủ hiển thị các thay đổi trước GA.
-
Ưu tiên công cụ đo lường cấp AST/nguồn để có khả năng di chuyển giữa các phiên bản.