#10016·borg

helpers layering: split parseformat, dissolve the helpers/__init__ shim, make imports point downward again

Author: ThomasWaldmannCreated Aug 2, 2026Updated Sep 11, 2026

From a global code review of the borg2 codebase (2026-08).

Problem

borg.helpers is supposed to be the bottom layer, but it imports upward: helpers/parseformat.py pulls in ..archive, ..manifest, ..compress, ..platform, ..item (inside functions), and cache.py/archive.py import each other. A rough count finds ~175 function-level from ..x import y statements in non-test code, most of them circular-import workarounds rather than genuine lazy loading.

The root cause: parseformat.py (1564 lines, the biggest helpers module) mixes true low-level utilities (bin_to_hex, size/time formatting) with high-level domain formatting (ArchiveFormatter, ItemFormatter, DiffFormatter) that inherently needs Archive/ItemDiff/Manifest. Meanwhile helpers/__init__.py re-exports ~100 names "for compatibility" — internal API compatibility is irrelevant for the breaking 2.0 release, and the shim encourages treating helpers as one big grab-bag.

Proposed direction

  • Move ArchiveFormatter/ItemFormatter/DiffFormatter (and other domain-aware formatting) up and out of helpers, e.g. into borg/output.py or borg.archiver.
  • Move Location parsing near the repository layer where it conceptually belongs.
  • Import from the specific helpers submodules directly; shrink helpers/__init__ to little or nothing.
  • Afterwards, most of the ~175 deferred imports can return to module top level.
  • Add an import-linter contract (tox/CI) that pins the layer order (constants → helpers → crypto/chunkers → repository → cache/archive → archiver) so cycles cannot silently regrow.

Generated with Claude Code