From 9f978e91d90a36ad3ee27e907369d9c9c8a92615 Mon Sep 17 00:00:00 2001 From: fennr Date: Wed, 28 May 2025 16:53:48 +0500 Subject: [PATCH] Add B043: Do not call delattr with constant --- README.rst | 5 +++++ bugbear.py | 13 +++++++++++++ tests/eval_files/b043.py | 11 +++++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/eval_files/b043.py diff --git a/README.rst b/README.rst index d67e4aa..a3b35db 100644 --- a/README.rst +++ b/README.rst @@ -288,6 +288,11 @@ second usage. Save the result to a list if the result is needed multiple times. **B041**: Repeated key-value pair in dictionary literal. Only emits errors when the key's value is *also* the same, being the opposite of the pyflakes like check. +.. _B043: + +**B043**: Do not call ``delattr(x, 'attr')``, instead use ``del x.attr``. +There is no additional safety in using ``delattr`` if you know the attribute name ahead of time. + Opinionated warnings ~~~~~~~~~~~~~~~~~~~~ diff --git a/bugbear.py b/bugbear.py index 49e658d..f876b8c 100644 --- a/bugbear.py +++ b/bugbear.py @@ -523,6 +523,13 @@ def visit_Call(self, node) -> None: and not iskeyword(node.args[1].value) ): self.add_error("B010", node) + elif ( + node.func.id == "delattr" + and len(node.args) == 2 + and _is_identifier(node.args[1]) + and not iskeyword(node.args[1].value) + ): + self.add_error("B043", node) self.check_for_b026(node) self.check_for_b028(node) @@ -2332,6 +2339,12 @@ def __call__(self, lineno: int, col: int, vars: tuple[object, ...] = ()) -> erro message="B040 Exception with added note not used. Did you forget to raise it?" ), "B041": Error(message=("B041 Repeated key-value pair in dictionary literal.")), + "B043": Error( + message=( + "B043 Do not call delattr with a constant attribute value, " + "it is not any safer than normal property access." + ) + ), # Warnings disabled by default. "B901": Error( message=( diff --git a/tests/eval_files/b043.py b/tests/eval_files/b043.py new file mode 100644 index 0000000..869b9c3 --- /dev/null +++ b/tests/eval_files/b043.py @@ -0,0 +1,11 @@ +# Valid usage +attr_name = "name" +delattr(obj, attr_name) +for field in fields_to_remove: + delattr(obj, field) +delattr(obj, some_name()) +delattr(obj, f"field_{index}") + +# Invalid usage +delattr(obj, "name") # B043: 0 +delattr(obj, r"raw_attr") # B043: 0 \ No newline at end of file