From c0580c10ca45bfa58a7cbe5e5f4e222d94506d34 Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Fri, 14 Aug 2026 13:49:44 +0200 Subject: [PATCH 1/2] fix: Extend the ctypes.Structure exception to Big/Little Endian and the Unions too. --- .../resources/test/fixtures/ruff/RUF012.py | 39 +++++++++++++ crates/ruff_linter/src/rules/ruff/helpers.rs | 10 +++- ..._rules__ruff__tests__RUF012_RUF012.py.snap | 56 +++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py index e5ec4d610ba51..e154ee871d8f2 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py @@ -150,3 +150,42 @@ class S(ctypes.Structure): ("propagation", ctypes.c_uint64), ("userns_fd", ctypes.c_uint64), ] + +class LES(ctypes.LittleEndianStructure): + test = [""] + _fields_ = [ + ("attr_set", ctypes.c_uint64), + ("attr_clr", ctypes.c_uint64), + ("propagation", ctypes.c_uint64), + ("userns_fd", ctypes.c_uint64), + ] + +class BES(ctypes.BigEndianStructure): + test = [""] + _fields_ = [ + ("attr_set", ctypes.c_uint64), + ("attr_clr", ctypes.c_uint64), + ("propagation", ctypes.c_uint64), + ("userns_fd", ctypes.c_uint64), + ] + +class U(ctypes.Union): + test = [""] + _fields_ = [ + ("a", LES), + ("b", BES), + ] + +class LEU(ctypes.BigEndianUnion): + test = [""] + _fields_ = [ + ("a", LES), + ("b", BES), + ] + +class BEU(ctypes.BigEndianUnion): + test = [""] + _fields_ = [ + ("a", LES), + ("b", BES), + ] diff --git a/crates/ruff_linter/src/rules/ruff/helpers.rs b/crates/ruff_linter/src/rules/ruff/helpers.rs index ba28950a92d54..b6f4b9defc759 100644 --- a/crates/ruff_linter/src/rules/ruff/helpers.rs +++ b/crates/ruff_linter/src/rules/ruff/helpers.rs @@ -252,7 +252,15 @@ pub(super) fn is_ctypes_structure_fields( ) -> bool { let is_ctypes_structure = analyze::class::any_qualified_base_class(class_def, semantic, |qualified_name| { - matches!(qualified_name.segments(), ["ctypes", "Structure"]) + matches!( + qualified_name.segments(), + ["ctypes", "Structure"] + | ["ctypes", "BigEndianStructure"] + | ["ctypes", "LittleEndianStructure"] + | ["ctypes", "Union"] + | ["ctypes", "BigEndianUnion"] + | ["ctypes", "LitleEndianUnion"] + ) }); let is_fields = matches!( diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap index 5cc2fd8c41a52..49bde9f36a389 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap @@ -1,5 +1,6 @@ --- source: crates/ruff_linter/src/rules/ruff/mod.rs +assertion_line: 133 --- RUF012 Mutable default value for class attribute --> RUF012.py:9:34 @@ -139,3 +140,58 @@ RUF012 Mutable default value for class attribute 148 | ("attr_set", ctypes.c_uint64), | help: Consider initializing in `__init__` or annotating with `typing.ClassVar` + +RUF012 Mutable default value for class attribute + --> RUF012.py:155:12 + | +154 | class LES(ctypes.LittleEndianStructure): +155 | test = [""] + | ^^^^ +156 | _fields_ = [ +157 | ("attr_set", ctypes.c_uint64), + | +help: Consider initializing in `__init__` or annotating with `typing.ClassVar` + +RUF012 Mutable default value for class attribute + --> RUF012.py:164:12 + | +163 | class BES(ctypes.BigEndianStructure): +164 | test = [""] + | ^^^^ +165 | _fields_ = [ +166 | ("attr_set", ctypes.c_uint64), + | +help: Consider initializing in `__init__` or annotating with `typing.ClassVar` + +RUF012 Mutable default value for class attribute + --> RUF012.py:173:12 + | +172 | class U(ctypes.Union): +173 | test = [""] + | ^^^^ +174 | _fields_ = [ +175 | ("a", LES), + | +help: Consider initializing in `__init__` or annotating with `typing.ClassVar` + +RUF012 Mutable default value for class attribute + --> RUF012.py:180:12 + | +179 | class LEU(ctypes.BigEndianUnion): +180 | test = [""] + | ^^^^ +181 | _fields_ = [ +182 | ("a", LES), + | +help: Consider initializing in `__init__` or annotating with `typing.ClassVar` + +RUF012 Mutable default value for class attribute + --> RUF012.py:187:12 + | +186 | class BEU(ctypes.BigEndianUnion): +187 | test = [""] + | ^^^^ +188 | _fields_ = [ +189 | ("a", LES), + | +help: Consider initializing in `__init__` or annotating with `typing.ClassVar` From 5f7869baff03ab345061fb8cb2f5bbf308c0e82f Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Fri, 14 Aug 2026 15:53:55 +0200 Subject: [PATCH 2/2] fixup: Fix typo and error in test snapshot. --- .../resources/test/fixtures/ruff/RUF012.py | 2 +- crates/ruff_linter/src/rules/ruff/helpers.rs | 15 +++++++++------ ...ter__rules__ruff__tests__RUF012_RUF012.py.snap | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py index e154ee871d8f2..af37af2333fb6 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py @@ -176,7 +176,7 @@ class U(ctypes.Union): ("b", BES), ] -class LEU(ctypes.BigEndianUnion): +class LEU(ctypes.LittleEndianUnion): test = [""] _fields_ = [ ("a", LES), diff --git a/crates/ruff_linter/src/rules/ruff/helpers.rs b/crates/ruff_linter/src/rules/ruff/helpers.rs index b6f4b9defc759..a6ebb82fb16bb 100644 --- a/crates/ruff_linter/src/rules/ruff/helpers.rs +++ b/crates/ruff_linter/src/rules/ruff/helpers.rs @@ -254,12 +254,15 @@ pub(super) fn is_ctypes_structure_fields( analyze::class::any_qualified_base_class(class_def, semantic, |qualified_name| { matches!( qualified_name.segments(), - ["ctypes", "Structure"] - | ["ctypes", "BigEndianStructure"] - | ["ctypes", "LittleEndianStructure"] - | ["ctypes", "Union"] - | ["ctypes", "BigEndianUnion"] - | ["ctypes", "LitleEndianUnion"] + [ + "ctypes", + "Structure" + | "BigEndianStructure" + | "LittleEndianStructure" + | "Union" + | "BigEndianUnion" + | "LittleEndianUnion" + ] ) }); diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap index 49bde9f36a389..fdd09af79c1c9 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap @@ -177,7 +177,7 @@ help: Consider initializing in `__init__` or annotating with `typing.ClassVar` RUF012 Mutable default value for class attribute --> RUF012.py:180:12 | -179 | class LEU(ctypes.BigEndianUnion): +179 | class LEU(ctypes.LittleEndianUnion): 180 | test = [""] | ^^^^ 181 | _fields_ = [