1 điểm bởi GN⁺ 1 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Ruff v0.16.0, bộ linter·formatter Python viết bằng Rust, tăng số quy tắc được bật mặc định từ 59 lên 413, giúp phát hiện rộng hơn các lỗi cú pháp và lỗi runtime xảy ra ngay cả khi không cần cấu hình riêng
  • Hỗ trợ định dạng khối mã Python trong Markdown được đánh dấu bằng python, py, pyi, pycon..., và cũng có thể áp dụng cho notebook Quarto
  • Bổ sung ruff: ignoreruff: file-ignore để chặn chẩn đoán trên từng dòng mã logic hoặc toàn bộ tệp, đồng thời có thể tự động chèn chú thích bằng --add-ignore
  • checkformat --check hiển thị các thay đổi sửa lỗi dưới dạng diff bên dưới chẩn đoán theo mặc định, và kiểm tra formatter cũng hỗ trợ JSON cùng định dạng đầu ra cho chú thích CI của GitHub·GitLab
  • Phần lớn có thể nâng cấp mà không cần thay đổi lớn, nhưng cần kiểm tra ảnh hưởng của việc tăng quy tắc mặc địnhthay đổi đầu ra JSON khi một số giá trị có thể trở thành null đối với cấu hình hiện có và công cụ tự động hóa

Mở rộng lên 413 quy tắc mặc định

  • Ruff v0.16.0 là bộ linter·formatter Python tốc độ cao viết bằng Rust, có thể cài qua PyPI hoặc uv tool install ruff@latest
  • Tổng số quy tắc của Ruff đã tăng từ 708 ở thời điểm v0.1.0 lên 968, nhưng số quy tắc bật mặc định vẫn được giữ ở 59 cho tới nay
  • v0.16 mở rộng số quy tắc mặc định lên 413, phát hiện các vấn đề nghiêm trọng như lỗi cú pháp và lỗi runtime xảy ra ngay mà không cần cấu hình riêng
    • Bao gồm các quy tắc thuộc nhóm B của flake8-bugbear, UP của pyupgrade, và nhóm RUF riêng của Ruff
    • Có thể xem toàn bộ danh sách trong tài liệu Default Rules
  • Các dự án đã dùng select hoặc extend-select cũng có thể tìm thấy những quy tắc hữu ích trước đây chưa biết thông qua bộ quy tắc mặc định mới
  • Để quay lại bộ quy tắc mặc định cũ, cấu hình như sau
[lint]
select = ["E4", "E7", "E9", "F"]
  • Thay đổi lần này gắn với nhiệm vụ dài hạn là phân loại lại quy tắc, và các công việc liên quan dự kiến vẫn sẽ tiếp tục

Định dạng khối mã Markdown

  • ruff format sẽ định dạng khối mã Python dạng fenced code block nằm trong tệp Markdown
  • Các chuỗi thông tin được hỗ trợ là python, py, python3, py3, pyi, pycon
    • pyi được xử lý theo định dạng tệp stub
    • pycon được xử lý theo định dạng phiên REPL
    • Các loại còn lại được định dạng như tệp Python thông thường
  • Ngay cả khi tên ngôn ngữ được bao bởi dấu ngoặc nhọn như {python} thì vẫn được nhận diện, nên cũng có thể dùng cho notebook Quarto
    • Nếu dùng phần mở rộng .qmd thì có thể cần cấu hình ánh xạ extension
  • Bên trong khối mã, có thể dùng fmt: offfmt: on để chặn định dạng một phần
  • Có thể loại trừ toàn bộ vùng tài liệu Markdown bằng chú thích HTML <!-- fmt: off --><!-- fmt: on -->
  • Để loại trừ mọi tệp Markdown, chỉ định glob như *.md trong extend-exclude
  • Có thể xem hành vi chi tiết trong tài liệu Markdown code formatting

Chú thích chặn chẩn đoán mới

  • Tiếp nối cơ chế chặn theo phạm vi ruff: disable·ruff: enable của v0.15, v0.16 bổ sung ruff: ignoreruff: file-ignore
  • ruff: ignore có thể chặn chẩn đoán trên cùng dòng như noqa, hoặc viết thành chú thích độc lập để áp dụng cho toàn bộ dòng logic kế tiếp
    • Trong phần khai báo hàm viết nhiều dòng, từ def đến dấu hai chấm được xem là một dòng logic
  • ruff: file-ignore sẽ chặn các chẩn đoán được chỉ định trên toàn bộ tệp, tương tự ruff: noqa
  • Mỗi chú thích chặn có thể ghi lý do áp dụng sau mã quy tắc
  • Tùy chọn CLI --add-ignore sẽ tự động thêm các chú thích ruff: ignore cần thiết
  • Ở chế độ preview, có thể dùng tên quy tắc như unused-import thay cho mã như F401
  • Toàn bộ đặc tả chú thích được tổng hợp trong tài liệu Ruff linter

Diff sửa lỗi và định dạng đầu ra

  • checkformat trước đây cũng hỗ trợ --diff, nhưng hoạt động tách rời với chẩn đoán thông thường nên không hiển thị cùng chẩn đoán giải thích lý do sửa
  • Đầu ra mặc định full của v0.16 hiển thị các thay đổi do linter·formatter có thể thực hiện dưới dạng diff bên dưới chẩn đoán
  • format --check cũng có thể dùng toàn bộ các định dạng đầu ra mà linter hỗ trợ
    • Có thể tạo JSON để máy đọc
    • Có thể xuất định dạng mà GitHub và GitLab hiển thị thành chú thích trong CI
  • Có thể xem các định dạng được hỗ trợ trong trợ giúp CLI và tài liệu output format

Tương thích và ổn định hóa

  • Số thay đổi phá vỡ tương thích trong v0.16 là ít, nên phần lớn có thể cập nhật mà không cần thay đổi lớn về mã hoặc cấu hình
  • Trong đầu ra JSON, filename, location, end_location, fix.edits[].location, fix.edits[].end_location có thể trở thành null thay vì dùng chuỗi rỗng hoặc dòng 1 cột 1 làm giá trị mặc định
    • Hiện tại số chẩn đoán bị ảnh hưởng là rất ít, nhưng trong các quy tắc tương lai điều này có thể trở nên phổ biến hơn
  • 12 quy tắc được chuyển từ preview sang trạng thái ổn định
    • Tương thích chữ ký hàm Airflow 3 AIR303, thông báo bản quyền CPY001, chuyển đổi float FURB164, min/max đã sắp xếp FURB192
    • Nối chuỗi trong collection literal ISC004, ghi log ngoại lệ ngoài exception handler LOG004, kiểu trả về bool sai PLE0304
    • Quá nhiều đối số vị trí PLR0917, trả về StopIteration PLR1708, vị trí None trong Union RUF036
    • Truy cập annotation trong từ điển lớp RUF063, mục trùng lặp trong __all__ RUF068
  • Hành vi đã được ổn định hóa của một số quy tắc hiện có cũng được áp dụng mặc định
    • BLE001 cũng sẽ được chặn khi ghi log ngoại lệ bằng các phương thức logging ngoài critical, error, exception
    • FA102 kiểm tra thêm các API tương thích PEP 585 như collections.abc
    • INT001·INT002·INT003 cũng kiểm tra các cách dùng phổ biến như gán gettext vào builtins._
    • S310 giảm false positive bằng cách phân giải liên kết literal chuỗi cục bộ
    • S508·S509 hỗ trợ API được khuyến nghị của PySNMP mới nhất
    • UP019 nhận diện không chỉ typing.Text mà cả typing_extensions.Text
  • Có thể xem toàn bộ thay đổi trong GitHub release

1 bình luận

 
Ý kiến trên Hacker News
  • Tôi đã nâng một dự án Python khoảng 3 nghìn dòng từ v0.15.x lên phiên bản mới, không mất nhiều thời gian, và nó còn tìm ra khá nhiều vấn đề mà bản trước bỏ sót nên chất lượng mã cũng tốt hơn
    Sửa thủ công theo gợi ý: https://github.com/nickjj/plutus/commit/9af66d31f98bef841588...
    Bật lại quy tắc độ dài dòng: https://github.com/nickjj/plutus/commit/21789f89bbcee37913c1...
    Bắt buộc tiền tố _ cho biến không dùng: https://github.com/nickjj/plutus/commit/a272c77b932e1c78558a...
    Ruff tự động sửa: https://github.com/nickjj/plutus/commit/6fe69cf88385ebdf9b8c...

  • Thật mừng khi sau khi Astral được OpenAI mua lại, Ruff, ty, uv vẫn đang được phát triển rất tích cực

    • Tôi từng kỳ vọng vào ty, nhưng nó thua khá xa basedpyright nên cuối cùng đã ngừng dùng. Vấn đề chí mạng không phải là thiếu hạng mục kiểm tra mà là quá nhiều cảnh báo sai, và nó cũng không có tính năng baseline rất hữu ích cho codebase lớn
      uv và Ruff thì rất tuyệt, và hy vọng một ngày nào đó ty cũng đạt tới mức đó
  • Thật ngạc nhiên khi mọi người lại hào hứng đến vậy với những công cụ cảnh sát cú pháp tự đặt ra đủ kiểu quy tắc và thậm chí còn không thống nhất được thế nào là mã Python tốt
    Chúng gộp dictionary nhiều dòng thành một dòng làm hỏng ý nghĩa của comment, rồi chỉ sửa những thứ vụn vặt như hai dấu cách hay dấu ngoặc kép. Vấn đề thực sự không phải khoảng trắng cuối dòng hay sắp xếp import, mà là những list comprehension dài 10 dòng khó hiểu, và các công cụ này lại không bắt được
    Ở công ty tôi đã dùng pylint, flake8, black và Ruff, và mỗi thay đổi lại sinh ra hàng trăm commit; có lẽ dùng năng lượng đó vào việc khác sẽ tốt hơn

    • Mục đích của những công cụ này là giúp mọi người tập trung vào vấn đề thật sự. Nếu tự động hóa các quyết định lint thì không cần lãng phí đầu óc để tranh cãi về định dạng trong PR nữa
      Nếu cứ chấp nhận kết quả chạy tự động thì có thể đứng ngoài các cuộc tranh luận về lint, nhưng ở những tổ chức không có các công cụ này thì người ta đã phải bỏ thời gian thật để suy nghĩ và tranh luận về định dạng
    • Sự ác cảm cho rằng linter chỉ làm tốn thời gian dường như đã vượt quá mức hợp lý. Trên thực tế có đủ bằng chứng cho thấy nó giúp tiết kiệm thời gian; rốt cuộc điểm chính chỉ là thỉnh thoảng linter tạo ra thay đổi mà bạn không thích
      Trong làm việc nhóm, thay vì quá khăng khăng với sở thích cá nhân, có lẽ nên lắng nghe người xung quanh và xem lại thứ tự ưu tiên giữa cộng tác và tinh thần thủ công
    • Ruff nhận biết comment cuối dòng nên vẫn giữ từng mục trên dòng riêng và chỉ thêm dấu phẩy cuối và khoảng trắng. Nếu có dấu phẩy cuối thì nó cũng không gộp dòng; kết quả được đưa ra có vẻ là từ Black
      Việc gộp dòng khi có comment theo dòng có vẻ không phù hợp. Dấu ngoặc kép trong Python chỉ là một lựa chọn phong cách đơn thuần, và nếu bên trong dấu nháy đơn có dấu ngoặc kép thì Ruff cũng sẽ để nguyên
    • Những công cụ này ngược lại còn giúp tiết kiệm năng lượng của cả nhóm. Nếu không có công cụ, mỗi lập trình viên sẽ có tiêu chuẩn khác nhau về định dạng, chất lượng mã và tính dễ đọc, nên sẽ tranh luận bất tận; tốt hơn là giao cho Ruff
    • Sở dĩ nó bị gộp thành một dòng là vì bạn đã quên dấu phẩy sau mục cuối cùng. Ít nhất với Black, nếu giữ dấu phẩy cuối thì các mục sẽ không bị nén lại, nhưng vì nó thường xuyên phá vỡ định dạng tôi chủ ý nên tôi không còn gắn nó vào mã của mình nữa
  • Giá mà Go cũng có một công cụ như Ruff. Nhiều ngôn ngữ có công cụ rất tốt, nhưng hệ sinh thái Go bị phân tán và không có công cụ nào cho cảm giác hoàn thiện như Ruff, Oxc, Biome, Mago

    • Go có Go Analysis Framework tốt hơn: https://pkg.go.dev/golang.org/x/tools/go/analysis
      Nó tương đối mới nên ít được biết đến hơn, nhưng là nền tảng của go fix và go vet, và có vẻ nhóm Go đang làm để tác giả module có thể dễ dàng định nghĩa các custom analysis pass tự động chạy khi thực thi go fix
      Với struct analysis.Analyzer, bạn có thể truy cập AST, thông tin kiểu, SSA và kết hợp thông tin giữa các analyzer; chỉ cần biên dịch thành binary rồi truyền cho go fix, phần toolchain sẽ xử lý cả việc cache phức tạp. Vì do chính nhóm Go tạo ra và tích hợp vào toolchain, các công cụ như golangci-lint cũng rất có thể về lâu dài sẽ hợp nhất theo framework này
      Bạn cũng có thể bảo AI agent viết Go Analysis analyzer rồi chạy bằng go fix, và trong dự án của tôi nó còn được dùng để tự động ép thực thi nhiều quy tắc một cách quyết định luận thay cho các chỉ dẫn Markdown thiếu chính xác
    • Cho đến không lâu trước đây, bầu không khí còn hoàn toàn ngược lại: cộng đồng Python khổ sở vì thiếu công cụ và ai cũng muốn có gofmt cho Python. Ruff không phải formatter mà là linter, nhưng hướng phát triển gần đây của hệ sinh thái Python vẫn rất đáng mừng
    • Tôi không thực sự hiểu ý nói hệ sinh thái Go bị phân tán. Go có công cụ định dạng và lint chính thức, và bản thân ngôn ngữ cũng được cố ý giới hạn để ngay cả người mới viết cũng ra một hình thức tương đối thống nhất
      Khác với Python hay TypeScript, nơi công cụ chính thức không ép một style cụ thể, ở Go khó có được hiệu ứng mạnh như lần đầu dùng Ruff hay Biome
    • Go là một trong những hệ sinh thái công cụ ngôn ngữ tốt nhất có thể dùng mà không cần IDE khổng lồ, và golangci-lint cũng khá toàn diện. Bản phân phối Go tự nó đã giải quyết được rất nhiều thứ
    • golangci-lint đã tồn tại từ lâu và được dùng rất rộng rãi
  • Việc bật 413 quy tắc mặc định là một thay đổi tích cực vì đa số dự án có thể nhận được lint hữu ích mà không cần đụng vào cấu hình

    • Nhưng cũng khó nói việc một dự án hiện có bỗng dưng nhận 413 cảnh báo tiềm ẩn có thực sự hữu ích hay không; có vẻ phù hợp hơn với dự án mới
      Dạo này có thể giao cho agent sửa toàn bộ cảnh báo lint theo một tiêu chuẩn định sẵn rồi để vài tiếng là xong, nhưng bản thân các chẩn đoán chi tiết mà Ruff cung cấp vẫn rất đáng hoan nghênh
  • Ruff cũng cần một tính năng giống stateVersion của Nix để quyết định bộ giá trị mặc định sẽ áp dụng. Khi cập nhật Ruff trên nhiều kho lưu trữ, mỗi lần có thêm quy tắc mặc định mới thì phải tắt ngay hoặc sửa vi phạm, nên rất khó dự đoán kết quả
    Cũng có thể ghi toàn bộ quy tắc cần bật vào danh sách cho phép, nhưng tốt hơn là giữ cấu hình đơn giản và chỉ nâng phiên bản trạng thái khi mọi người đều có thể dành vài giờ cho việc đó

    • Cách phù hợp hơn là cố định phiên bản Ruff mong muốn trong pyproject.toml cho từng dự án. Khi từng dự án sẵn sàng thì nâng phiên bản, như vậy không cần điều phối nhiều dự án cùng lúc, và nếu một cái bị chậm lại thì cũng không chặn phần còn lại
    • Theo nguyên văn, bộ quy tắc mặc định của Ruff đã không thay đổi suốt hơn 2 năm, và lần thay đổi gần nhất là v0.1.0
      https://github.com/astral-sh/ruff/releases/tag/v0.1.0
  • Trong thời đại coding agent, linting mạnh quan trọng hơn bao giờ hết, và tôi muốn thấy nhiều công cụ như forbidigo hơn ở các ngôn ngữ khác

    • Tôi đang nâng cấp các dự án nhưng cũng có cảm xúc lẫn lộn. Khi tự viết mã, tôi có thể dựa vào trực giác để quyết định lúc nào nên bỏ qua hay phớt lờ một quy tắc, nhưng ở những dự án bật hầu hết quy tắc pylint thì mã lại trở nên khó đọc hơn vì các mẹo lách luật chỉ để làm hài lòng pylint
      Coding agent cũng tiêu tốn rất nhiều token để sửa các vấn đề vụn vặt, hoặc nếu test thất bại thì thậm chí vô hiệu hóa hẳn. Tôi đã bắt đầu tin vào độ chính xác tổng thể của kết quả AI, nhưng khả năng phán đoán về chất lượng mã thì vẫn rất khó tin tưởng
  • Dù đã có tới 413 quy tắc, mỗi lần tham gia một codebase mới tôi vẫn phải lặp lại y hệt ba cuộc tranh luận về việc sắp xếp import

  • Giờ có vẻ như cách dùng không cần cấu hình đang được khuyến nghị, điều này thật đáng mừng. Với .ruff.toml mới, chỉ cần để line-length = 300 là đủ