Skip to content

Adopt module-path privacy convention (ADR-012)#648

Open
ecomodeller wants to merge 3 commits into
mainfrom
public-private-api-convention
Open

Adopt module-path privacy convention (ADR-012)#648
ecomodeller wants to merge 3 commits into
mainfrom
public-private-api-convention

Conversation

@ecomodeller
Copy link
Copy Markdown
Member

Summary

Establishes a written convention distinguishing public API from internal API, and aligns the codebase with it.

Policy (full text in ADR-012): Privacy is set by the module path, not the function name. A name is private if any segment of its import path starts with _. Functions inside private modules use plain names — the path already carries the signal. This matches the scikit-learn convention (sklearn.utils._param_validation.validate_params).

Enforced by Ruff PLC2701 (import-private-name), which now runs in CI and reports zero violations in src/. Tests have a per-file ignore.

Concrete changes:

  • Functions inside private modules (_timeseries.py, _misc.py, _base.py, _point.py, _track.py, _vertical.py, comparison/_utils.py) drop their leading _.
  • Cross-cutting helpers that lived in public modules move to private homes: new modelskill/_utils.py for get_name, get_idx, RESERVED_NAMES, linear_regression; parse_metric moves to comparison/_utils.py.
  • modelskill/timeseries/__init__.py no longer re-exports the five _parse_*_input helpers in __all__ (they were simultaneously public-by-__all__ and private-by-name). Importers in model/*.py and obs.py now use the deep private paths.
  • modelskill.utils keeps its currently-reachable public surface (rename_coords_xr, make_unique_index, etc.) — no breaking changes to non-underscored import paths.

Test plan

  • CI green (lint, mypy, pytest, doctest)
  • ruff check src/ reports 0 PLC2701 violations
  • Public top-level surface unchanged: sorted(n for n in dir(modelskill) if not n.startswith('_')) matches main
  • from modelskill.utils import rename_coords_xr still works
  • from modelskill.timeseries import _parse_track_input correctly raises ImportError (intentionally removed)

Function names inside private modules drop the leading underscore; the
module path already signals "internal." Cross-cutting helpers that lived
in public modules (utils.py, metrics.py) move into a new modelskill/_utils.py
or into comparison/_utils.py so they don't accidentally become public surface.
Pyright's reportPrivateUsage warnings disappear; Ruff PLC2701 now enforces
the rule in CI.

See adr/012-public-private-api-convention.md for the policy and rationale.
- Soften ADR-012 + CLAUDE.md to describe what's actually enforced:
  PLC2701 is the hard rule; underscores on function names are an
  optional convention.
- Split _utils.RESERVED_NAMES into RESERVED_COORD_NAMES (coord-name
  collisions) and RESERVED_COMPARER_VAR_NAMES (+Observation slot),
  expressing the superset relationship in code instead of by
  shadowed in-function constants.
- Extract linear_regression from _utils.py into its own
  single-purpose _regression.py, leaving _utils.py as the
  name-handling helpers module.
"utils" is non-informative — the module's actual concern is reserved
coordinate/variable names and name-or-index resolution. Naming it _names.py
says what's inside and what isn't. ADR-012 updated to reflect the rename and
justify the choice; CLAUDE.md example updated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant