Show HN: Wat – Trình kiểm tra chuyên sâu đối tượng Python
(github.com/igrek51)- WAT là một inspector để nhanh chóng xác định bản chất của các đối tượng chưa rõ trong runtime Python, cho phép xem cùng lúc kiểu, giá trị, thuộc tính, phương thức, kiểu cha, chữ ký, tài liệu và cả mã nguồn
- Cách dùng cơ bản là
wat / object, hoạt động tương đương vớiwat(object), đồng thời hỗ trợ nhiều cú pháp nhưwat.short / 'foo','foo' | wat.short,wat('foo', short=True) - Có thể nối chuỗi các modifier như
.short,.dunder,.long,.code,.caller,.public,.all,.ret,.strđể điều chỉnh phạm vi hiển thị, cách trả về kết quả, đầu ra màu và hiển thị vị trí gọi - Cài đặt bằng
pip install watrồiimport wat, và để debug nhanh còn có thể dán snippet Insta-Load vào phiên Python để dùng ngay trong cùng phiên mà không cần cài đặt - Các ví dụ như Django
User,re.match,pathlib,colorsys.hsv_to_rgb,typing.List[str],str | Nonecho thấy WAT có thể được dùng để debug, khám phá trong REPL và học về nội bộ Python
WAT làm gì
- WAT là công cụ để khám phá và kiểm tra đối tượng Python ngay trong runtime
- Khi khó xác định một đối tượng chưa rõ là gì, có thể dùng inspector
wattrong console Python để điều tra bản chất của đối tượng đó - Với một
objectbất kỳ, chạywat / objectsẽ cho biết các thông tin sau- type của đối tượng
- giá trị đã được định dạng
- biến và phương thức
- kiểu cha
- chữ ký
- tài liệu
- mã nguồn
- Việc kiểm tra chuyên sâu tương tự cũng có thể thực hiện bằng cú pháp
wat(object) Watđược giới thiệu là một biến thể của từ tiếng Anhwhat, dùng để diễn tả sự bối rối hoặc khó chịu
Cách dùng cơ bản và cú pháp
- Dùng toán tử chia để nhập nhanh
wat / footương đương vớiwat(foo)
- Có nhiều cú pháp cho cùng một thao tác kiểm tra
wat.short / 'foo': cú pháp để nhập nhanhwat.short('foo')wat('foo', short=True): cú pháp Python tự nhiên'foo' | wat.short: cú pháp kiểu pipe Unix
- Có thể điều chỉnh hành vi kiểm tra với dạng
wat.modifier / foo - Các modifier có thể nối chuỗi với nhau, ví dụ
wat.short.str.gray / 'foo' - Trong Python, đối tượng không chỉ là cấu trúc dữ liệu mà còn bao gồm hàm, class, module, kiểu built-in..., nên
watcó thể khám phá mọi loại đối tượng - Gõ
wattrong interpreter sẽ hiển thị trợ giúp về chính đối tượngwat
Điều chỉnh phạm vi kiểm tra bằng Modifier
.shorthoặc.sẩn các thuộc tính như biến và phương thức bên trong đối tượng, chỉ in ra giá trị, kiểu, kiểu cha, chữ ký và tài liệu.dunderhiển thị các thuộc tính dunder bắt đầu bằng__.longhiển thị giá trị và docstring không bị rút gọn.codehiển thị mã nguồn của hàm, phương thức và class.nodocsẩn tài liệu của hàm và class.callerhiển thị cách và vị trí việc kiểm tra được gọi, hoạt động trong file chứ không phải REPL.publicẩn thuộc tính private và chỉ hiển thị thuộc tính public.allbao gồm mọi thông tin có thể.rettrả lại đối tượng sau khi kiểm tra.strtrả về chuỗi kết quả thay vì in ra.graytắt đầu ra màu trong console.colorép bật đầu ra màu trong consolewat.localskiểm tra biến cục bộ, cònwat.globalskiểm tra biến toàn cục
Cài đặt và Insta-Load
- Quy trình cài bằng pip như sau
pip install wat- trong Python,
import wat
- Gói
watkhông có phụ thuộc bên ngoài - Để debug nhanh, công cụ còn cung cấp cách Insta-Load giúp dùng ngay trong cùng phiên Python mà không cần cài đặt
- Insta-Load hoạt động bằng cách import
base64,zlib, sau đó khôi phục chuỗi mã đã được nén và mã hóa rồi chạy bằngexec(..., globals())trong một snippet Python dán vào interpreter - Sau khi chạy snippet Insta-Load, có thể sử dụng đối tượng
wat - Trước khi chạy snippet, nên kiểm tra nội dung sắp thực thi
- Có thể xem trước mã đã giải ra bằng
print(zlib.decompress(base64.b64decode(code)).decode()) - Cũng có thể dán nội dung của
inspection.pyvào interpreter để đạt hiệu quả tương tự - Một cách khác được đề xuất là cài package bằng pip và tự rà soát mã
- Có thể xem trước mã đã giải ra bằng
- WAT có thể được nạp từ một Unicode glyph duy nhất
- Loader dựa trên chuỗi Unicode sẽ chuyển chuỗi dài gồm emoji và ký tự kết hợp thành byte bằng
ord(c) & 255, sau đó chạyzlib.decompress(...)rồiexec(...)
Xác định kiểu đối tượng và cách sử dụng
- Trong Python là ngôn ngữ định kiểu động, đôi khi khó xác định kiểu của đối tượng; WAT Inspector sẽ hiển thị tên kiểu và module mà kiểu đó thuộc về
- Ví dụ kiểm tra kiểu cho thấy cả giá trị, kiểu và độ dài
wat.short / (1,)in ra giá trị(1,), kiểutuple, độ dài1wat.short / {None}in ra giá trị{None}, kiểuset, độ dài1
- Trong ví dụ với đối tượng Django
User,wat.short / userin rastr: admin,repr: <User: admin>, kiểudjango.contrib.auth.models.Uservà danh sách kiểu cha - Sau khi xác nhận được kiểu thực tế, có thể thêm type annotation vào code để giảm nhầm lẫn về sau
- Khi muốn hiểu cách dùng của một đối tượng chưa rõ, có thể in danh sách phương thức, chữ ký và docstring
- Ví dụ được đưa ra là
wat / ['foo'] - Nếu muốn xem toàn bộ docstring thì dùng
wat.long
- Ví dụ được đưa ra là
- Để nắm cách dùng của hàm, có thể xem docstring và chữ ký của hàm
- Ví dụ là
wat / str.split
- Ví dụ là
Khám phá thuộc tính, module và mã nguồn
- Để xem bên trong đối tượng đang kiểm tra, có thể liệt kê thuộc tính và kiểu của từng thuộc tính
- Ví dụ là
wat / re.match('(\\d)_(.*)', '1_title')
- Ví dụ là
- Công cụ cũng có thể dùng để khám phá module, liệt kê các hàm, class và submodule trong module đã chọn
- Có ví dụ chạy
import pathlibrồiwat / pathlib - Sau đó có thể đi sâu hơn như
wat / pathlib.fnmatch
- Có ví dụ chạy
- Theo mặc định, WAT Inspector ẩn các thuộc tính bắt đầu bằng
__- Có thể xem thuộc tính dunder bằng
wat.dunder / {}
- Có thể xem thuộc tính dunder bằng
- Để kiểm tra hàm thực sự hoạt động như thế nào, có thể xem mã nguồn của nó
- Ví dụ là chạy
import colorsysrồiwat.code / colorsys.hsv_to_rgb
- Ví dụ là chạy
- Các
dictvàlistlồng nhau sẽ được định dạng thụt lề để dễ đọc
Phiên debug và kiểm tra biến
- Có thể chạy debugger tương tác của Python bằng
breakpoint()rồi kiểm tra đối tượng ngay tại đó - Ví dụ Pdb cho thấy sau khi
import wathoặc dán snippet Insta-Load, có thể dùngwat / foođể kiểm tra biến cục bộ rồi nhấncđể tiếp tục chạy - Biến cục bộ và biến toàn cục có thể xem lần lượt bằng
wat.locals,wat.globals - Gọi
wat()không có đối số sẽ in các biến cục bộ trong stack của caller dưới tiêu đềLocal variables
Ví dụ học về nội bộ Python
- Có kèm các ví dụ phục vụ việc học cách Python vận hành bên trong
reversed([]) == reversed([])làFalse, vàwat.s / reversed([])cho thấy giá trị là đối tượnglist_reverseiteratorvà kiểu cũng làlist_reverseiteratorwat / type('ObjectCreator', (), {})hiển thị giá trị của class được tạo động, kiểutype, chữ kýsignature: class ObjectCreator()wat / typehiển thị giá trị của chínhtype, kiểutype, chữ kýclass type(…), tài liệutype(object) -> the object's type,type(name, bases, dict, **kwds) -> a new type, cùng các thuộc tính public nhưmrowat.s / List[str]hiển thị giá trịtyping.List[str], kiểutyping._GenericAlias, kiểu chatyping._BaseGenericAlias,typing._Final, chữ kýdef List(*args, **kwargs)wat(str | None)hiển thị giá trịstr | None, kiểutypes.UnionType- Các ví dụ khám phá đối tượng built-in của Python gồm
wat / __builtins__,wat / ... - Có thể kiểm tra cả chính WAT
- Ví dụ như
wat.dunder / wat,wat.code / wat.__truediv__
- Ví dụ như
Tóm tắt cách hoạt động bên trong
inspect_format(obj, *, short=False, dunder=False, nodocs=False, long=False, code=False, caller=False, public=False, all=False)tạo chuỗi kết quả kiểm tra đối tượng- Nếu
all=Truethìdunder,long,code,callersẽ cùng được bật - Nếu
public=Truethì đầu ra private sẽ bị tắt - Nếu
sys.stdout.isatty()là đúng, công cụ sẽ lấy độ rộng terminal và thêm các đường phân cách─ở trên và dưới đầu ra
- Nếu
- Kết quả kiểm tra được tạo theo thứ tự: giá trị đối tượng, biểu diễn chuỗi, kiểu, kiểu cha, độ dài, chữ ký, tài liệu, mã nguồn, rồi đến phần thuộc tính
- Việc kiểm tra thuộc tính sẽ duyệt
dir(obj)theo thứ tự tên- Thuộc tính dunder sẽ bị loại nếu tùy chọn
dunderđang tắt - Thuộc tính private bắt đầu bằng
_sẽ bị loại nếu tùy chọn private đang tắt - Nếu
getattr(obj, key)némBaseException, đối tượng ngoại lệ sẽ được dùng làm giá trị
- Thuộc tính dunder sẽ bị loại nếu tùy chọn
- Với đối tượng callable, công cụ định dạng chữ ký dựa trên
inspect.signature(obj)- Nếu thất bại, nó trả về chữ ký thay thế dạng
(...) - Với class sẽ thêm tiền tố
class, với coroutine function làasync def, còn hàm, phương thức, builtin hoặc đối tượng có__name__sẽ thêm tiền tốdef
- Nếu thất bại, nó trả về chữ ký thay thế dạng
- Khi
code=Truevà đối tượng là class hoặc callable, mã nguồn sẽ được lấy bằnginspect.getsource(obj)- Nếu xảy ra
OSError,TypeError,IndentationErrorthì sẽ trả về thông báo thất bại
- Nếu xảy ra
- Formatter cho dict và list sẽ trả về
ERROR: too deeply nestednếu độ sâu thụt lề vượt quá 30
Đầu ra màu và theme
- Có thể điều khiển đầu ra màu bằng biến môi trường
WAT_COLOR="false"tắt đầu ra màu trong consoleWAT_COLOR="true"ép bật màu ngay cả trong môi trường non-tty
- Có thể tùy biến theme màu bằng biến môi trường
WAT_COLORS - Theme mặc định là ánh xạ mã màu ANSI dạng
BAR=0;34,TRAIT=1;34,HEAD=1;37,STR=0;32,NUMBER=0;31,NONE=0;35,TRUE=1;32,FALSE=1;31,DOCS=2;37,KEYWORD=0;34,CALLABLE=1;32,VARIABLE=1;33,CODE=0;33 _strip_color(text)loại bỏ ANSI escape sequence bằng regex
Cảm hứng
- WAT lấy cảm hứng từ Rich Inspect
1 bình luận
Ý kiến trên Hacker News
Wow, rất hay. Trước đây tôi từng dùng python-ls[0] cho mục đích tương tự, nhưng vì lý do nào đó tôi không nhớ, nó bị hỏng và cũng không còn được bảo trì nữa
Tôi định thêm nó vào bộ công cụ debug của mình, vốn chủ yếu gồm snoop[1] và pdbpp. Điều tôi mong ở wat là một ipy widget giúp việc khám phá object trong Jupyter dễ hơn
Tôi cũng thích mẹo hack exec bằng base64. Dùng Python lâu rồi mà đến giờ tôi vẫn chưa từng nghĩ tới hay thấy cách này, nên chắc chắn sẽ thử dùng cho vài mục đích trong tương lai
[0] https://github.com/gabrielcnr/python-ls
[1] https://pypi.org/project/snoop/
Trông thú vị. Trong Python tôi luôn dùng dir, và khi tài liệu không tốt lắm thì đôi khi nó còn hữu ích hơn tài liệu chính thức
Interactive shell là một trong những điểm mạnh thật sự của Python, nên hơi ngạc nhiên là không có nhiều công cụ mới hay đổi mới hơn quanh nó
Trông giống một phiên bản hào nhoáng hơn của icecream cũ
https://github.com/gruns/icecream
Nếu chưa biết, bạn cũng có thể xem danh sách các bản triển khai cho ngôn ngữ khác ở phía dưới
https://github.com/gruns/icecream#icecream-in-other-language...
Những công cụ kiểu này rất hữu ích
20 năm trước tôi từng làm một object introspector cho Zope
Dạo này tôi dùng devtools hằng ngày, còn icecream và q thì thỉnh thoảng. Tôi cũng sẽ thử wat
from wat import watVới tính chất dự án ngầu như vậy, tôi hơi ngạc nhiên là nó không cung cấp luôn cú pháp
import watvới cách dùng tương tự. Như thế còn có thể khiến người dùng tò mò thử wat/wat rồi phát hiện ra trò nàyimport watthì hay thật, nhưng trong Python có hạn chế là không thể làm cho module gọi được như hàm. Vì vậy mới phải dùng dạng dài hơnfrom wat import watKhông chắc, nhưng
import wat; wat.wat / objectcó khi lại tiện hơnTrông rất hữu ích, nhưng không biết có phải chỉ mình tôi thấy khó chịu với xu hướng gần đây là nhân danh khả năng đọc mà overload các toán tử hoàn toàn không liên quan, ở đây là toán tử
/hay không/trong trường hợp này là một lựa chọn lạ. Dù vậy, cũng tiếc là không thể overloadis. Thực tế thì chỉwat(foo)có lẽ cũng đủ rồiNếu muốn tránh việc import phiền phức, bạn cũng có thể thêm đoạn sau vào file
$PYTHONSTARTUPtry:from wat import watexcept ImportError:passCuối cùng tôi in output của nó ra rồi đặt vào một thư mục mà
PYTHONPATHtrỏ tới, để lúc nào cũng dùng đượcCòn phải xem có tiếp tục dùng không
Wow, nếu có công cụ như thế này khi tôi học Python thì chắc đã thay đổi cục diện. Khi học một ngôn ngữ, việc nhìn thấy những gì đang diễn ra bên trong là con đường cốt lõi, còn debug mặc định của Python thì nói nhẹ nhất cũng là đáng thất vọng
Thay vào đó tôi đã cài pry và trở thành fan Ruby nhiệt thành, nhưng công cụ này có thể khiến tôi thử lại Python
Tác giả dùng module inspect của Python trong thư viện chuẩn ở bên trong để cung cấp chức năng. Tất nhiên là đã bổ sung rất nhiều giá trị gia tăng ở phía trên
Hãy xem
inspection.pycủa module watDòng thứ 2 có đoạn này:
import inspect as std_inspect“Nếu muốn debug nhanh thứ gì đó, bạn có thể dùng inspector này trong cùng phiên mà không cần cài đặt gì”
“Dán snippet này vào Python interpreter để load ngay tại chỗ”
Ý tưởng đưa toàn bộ bản sao dự án vào README của dự án dưới dạng dữ liệu nén mã hóa base64 khá sáng tạo
Đặc biệt phù hợp với những dự án kiểu này, nơi bạn có thể đã không nghĩ đến việc cài sẵn nó trong môi trường mà rồi chắc chắn sẽ cần tới