meaningless-name (SMP103)

SMP103No fixNamingAll rules

Flags variables, parameters and attributes named with a placeholder word that says nothing about what they hold, such as foo or baz. The words come from a list you can change.

Why is this bad?

A name is the only description a value carries at each place it is used. foo leaves the reader to find where it is assigned, and then to learn what it is. A name for the contents, such as counts, answers that at once.

Example

Flagged
1def load(payload: bytes) -> dict[str, int]:
2    foo = parse(payload)
3    return foo
Preferred
1def load(payload: bytes) -> dict[str, int]:
2    counts = parse(payload)
3    return counts

Configuration

  • naming.meaningless-names in [tool.simplint]: the words to report, foo, baz, qux and quux by default. Words such as data, result, value or tmp are left out: a parser’s feed(data), a for key, value in loop or a tmp directory reads well in context. A list replaces the default one, so a project that bans more words lists the defaults too.

    Example: configuration

    pyproject.toml

    1[tool.simplint.naming]
    2meaningless-names = ["foo", "baz", "tmp"]
    
    1tmp = load()
    

What it does

Reported names are the ones the list holds, compared as whole names, so foo_count is not foo. A word matches in lower case, or in upper case as a constant spells it, with or without leading or trailing underscores.

Active on

  • Variables: names bound by assignment, for loops, with ... as and the walrus operator.

    Example: reported
    1foo = 3
    
    Example: reported
    1_FOO = 3
    
    Example: reported
    1for foo in items:
    2    print(foo)
    
  • Parameters of a function or a lambda.

    Example: reported
    1def scale(factor: float, foo: float) -> float:
    2    return factor * foo
    
  • Exception and match names: the name except ... as and a case capture binds.

    Example: reported
    1try:
    2    run()
    3except OSError as foo:
    4    print(foo)
    
  • Attributes a method declares: self.name = ... in a method, or cls.name = ... in a classmethod.

    Example: reported
    1class Cache:
    2    def __init__(self) -> None:
    3        self.foo = {}
    

Exceptions

  • Overrides: the parameters of a method decorated with @override keep the names of the method they replace.

    Example: not reported

    pyproject.toml

    1[project]
    2requires-python = ">=3.12"
    
    1from json import JSONEncoder
    2from typing import override
    3
    4
    5class Encoder(JSONEncoder):
    6    @override
    7    def default(self, foo: object) -> object:
    8        return str(foo)
    
  • Dunder methods: the parameters of a method named with double underscores, such as value in __setitem__, keep the names of the protocol they implement.

    Example: not reported

    pyproject.toml

    1[tool.simplint.naming]
    2meaningless-names = ["value"]
    
    1class Store:
    2    def __setitem__(self, key: str, value: int) -> None:
    3        self.items = {key: value}
    
  • Attributes written through other objects: response.foo = ... names an attribute the code does not choose.

    Example: not reported
    1def fill(response) -> None:
    2    response.foo = b""
    
  • Names outside the list: a name that merely contains a listed word is a name with a meaning.

    Example: not reported
    1foo_count = load()
    
  • Class names: Foo names a class, which the rule does not check where it is defined, so a Foo = ... that rebinds one is not reported either.

    Example: not reported
    1Foo = make_class()
    

Reporting

Each name is reported once per scope, at its first binding, as single-letter-name reports its names.

Ruff overlap

Ruff’s ambiguous-variable-name covers l, O and I only. This rule covers a word list you choose, like pylint’s disallowed-name.

Fix

No fix is offered, since a meaningful name cannot be derived from the code.

Other tools