helpers layering: split parseformat, dissolve the helpers/__init__ shim, make imports point downward again
- Dominant language
- Python
- Stars
- 13.7k
- Forks
- 875
- Avg merge
- 11h 15m
- Merged PRs (30d)
- 192
Description
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](https://claude.com/claude-code)
Contributor guide
Assessment
This issue has not been assessed yet.