SLD9xx - Privacy

The checker enforces Python’s underscore-prefix convention across class attributes, module attributes, and submodule imports. Dunder names (__init__, __class__, __future__) are not flagged. Only the current package exposes a private surface you may reach: from . import _x binds an own private submodule, and from ._sub import public reads its public surface. Reaching up into an ancestor (from .. import _x, from .._sub import y) or sideways into a sibling’s private name (from .sub import _x) is flagged just like an absolute private import.

SLD901: External read of a private attribute. obj._attr is only permitted on the privileged first argument (self, cls, or whatever the method’s first parameter is named) of an instance or class method.

# Bad
def render(thing):
    return thing._cached_html

# Good
class Renderer:
    def render(self):
        return self._cached_html

SLD902: External write of a private attribute. Same predicate as SLD901 but for assignment, augmented assignment, and del. Tracked as a separate code so codebases can adopt different policies for reads and writes.

# Bad
session._token = new_token
del session._cache

SLD903: A private name imported across a package boundary. The only permitted way to import a private name is from . import _name, which binds the current package’s own private submodule. Any module part (from .sub import _x), any ancestor level (from .. import _x), and every absolute import (from pkg import _x) is flagged. To consume a private submodule’s contents, import its public names instead.

# Bad
from pkg import _internal_helper      # absolute
from .sub import _internal_helper     # sibling's private name
from .. import _internal_helper       # ancestor's private name

# Good
from . import _internal_helper        # own private submodule
from ._submodule import public        # own submodule's public surface

SLD904: An import reaches into a private submodule across a package boundary. A private segment (one starting with _, other than a dunder) in the dotted path is permitted only for the current package’s own private submodule — a single-dot relative import. Absolute paths and ancestor (.. or deeper) paths are flagged.

# Bad
from numpy._core import multiarray    # absolute
import numpy._core.umath              # absolute
from .._engine import compile         # ancestor's private submodule

# Good
from ._core import multiarray         # own private submodule

SLD905: Private attribute access on an imported name. The post-import counterpart to SLD903 and SLD904.

import numpy as np

# Bad
np._core.something

# Good
np.array(...)