Skip to content

shed-redundant-base

shed-redundant-base removes an explicit object base and the empty parentheses on a class header with no bases, so class C(object): and class C(): both read class C:. Every class on Python 3 inherits from object whether or not the header says so, and a header with no bases needs no parentheses, so both forms put tokens between the reader and the class name that say nothing once read.

The two forms are removed the same way because both leave a base list with nothing in it, so the parentheses go with the base rather than standing empty, and a space written between them and the class name goes too. An object beside another base is a narrower case, in that the base list still has a member, so only the object and the comma joining it to its neighbor go and the parentheses stay for what survives. Two object bases written side by side go as one span. A metaclass= keyword counts as a surviving member the same way a named base does. Where a base carries a grouping pair of its own, that pair goes with it, though reflow-parentheses has usually removed such a pair before this rule reads the header.

A base named object is only the builtin where the module has not rebound that name ahead of the class, so a module opening object = LegacyBase keeps every header written against it. A comment inside the span that would go keeps the header as written too, since removing the span would remove the comment, and that covers a comment beside an object base and one inside an otherwise-empty pair alike.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueTurns the rule on or off.

The Canonical Case

class Cache(object): and class Registry(object): each name object as their only base. The object base and the parentheses around it are removed from both, leaving class Cache: and class Registry:.

Every class inherits from object on Python 3, so the written base repeats what the bare header already states.

class Cache:
    def clear(self):
        self.entries.clear()


class Registry:
    def register(self, name):
        self.entries.append(name)
python

More Examples

class Plugin(object, metaclass=PluginMeta): lists object beside a metaclass keyword. object and the comma after it drop while the parentheses stay, leaving class Plugin(metaclass=PluginMeta):, because the keyword is valid only inside a base list.

Adapter lists its bases one per line with object, after Mapping,, and Wrapper lists object, ahead of metaclass=WrapperMeta,. The object, line drops from each list along with its trailing comma, leaving Mapping, and metaclass=WrapperMeta, at the indent they already had.

class Marker():, class Sentinel():, and class Spaced (): each carry an empty base list. The parentheses drop from all three, so the headers read class Marker:, class Sentinel:, and class Spaced:, and the space Spaced wrote before its parentheses goes with them so no gap is left ahead of the :.

class Layered(Mapping, Sized, object): and class Trailing(Mapping, object): each name a real base beside object. Only the object entry and the comma before it drop, leaving class Layered(Mapping, Sized): and class Trailing(Mapping): with their parentheses kept around the remaining bases.

No Change

Annotated lists object, on its own line beside the comment # spelled out for the reader, and Bare( carries # no bases at all inside its empty parentheses. Both headers stay exactly as written, because a deletion whose span contains a comment is skipped rather than applied, and removing the base or the parentheses here would remove the comment with it.

No Change

object = LegacyBase rebinds the name at module level, and class Consumer(object): follows it. The header stays exactly as written, because its object names that module-level binding rather than the builtin, and removing it would change which class Consumer inherits from.

For per-statement opt-outs, the Suppression chapter covers the # prose: skip[shed-redundant-base] directive, which covers every line a wrapped class header spans.