cadrumo.core.paths module¶
Shared path normalization helpers.
Centralises the small set of Path primitives that every other
Cadrumo module needs: resolving relative operator paths against the
application-data anchor via resolve_project_path(), normalising settings
with normalize_project_relative_path().
These helpers validate and compose paths only. They do not read, write, create, or secure files; persistence adapters that need registered storage errors own their typed containment layer.
is_windows_long_path_error(), windows_long_paths_enabled(), and
windows_storage_root_long_path_margin() are the Windows MAX_PATH
(260-character) hardening surface: classifying an OSError that legacy
Windows raises once a resolved path exceeds the limit, reading the
machine-wide long-path opt-in, and computing whether a candidate storage root
leaves enough headroom for the deepest object path the caller’s storage layout
can produce. The worst-case suffix that last helper measures against is
supplied by its caller: this module owns the arithmetic, never the on-disk
grammar of a layer above it.
- WINDOWS_MAX_PATH: int¶
Legacy Windows
CreateFileWpath-length ceiling in UTF-16 code units. Windows 10 1607+ can lift this per-application via theLongPathsEnabledregistry value combined with alongPathAwareapplication manifest; a workstation that has neither still enforces this ceiling.
- is_windows_long_path_error(exc)[source]¶
Return whether
excis a Windows path-length-ceiling failure.Classifies
WinError 3(ERROR_PATH_NOT_FOUND) andWinError 206(ERROR_FILENAME_EXCED_RANGE) — the two concrete Windows API error codes a legacy (non long-path-aware) workstation raises once a resolved path walks pastWINDOWS_MAX_PATH. AlwaysFalseon non-Windows platforms and for any otherOSError, so callers can unconditionally probe every caughtOSErrorwithout a platform guard of their own.- Parameters:
exc (
OSError) – The caughtOSError(or subclass, e.g.FileNotFoundError) to classify.- Return type:
- Returns:
Truewhenexc.winerrornames a known long-path failure.
- windows_long_paths_enabled()[source]¶
Report the machine-wide Windows long-path opt-in, if determinable.
Reads
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem \LongPathsEnabled— the registry value Windows 10 1607+ consults to liftWINDOWS_MAX_PATHfor manifest-declared long-path-aware applications (this CLI is built with alongPathAwaremanifest via its packaging).- Return type:
- Returns:
Truewhen the value is present and non-zero,Falsewhen present and zero (or when the platform is Windows but the value is absent — the pre-1607 / not-yet-opted-in default), andNoneon a non-Windows platform where the concept does not apply, or when the registry cannot be read at all (a probe is best-effort; it never raises).
- windows_storage_root_long_path_margin(root, *, object_path_suffix_length)[source]¶
Return the headroom, in characters, before an object write risks
MAX_PATH.Computes
WINDOWS_MAX_PATH - len(str(root.resolve())) - object_path_suffix_length. A positive result is the number of characters of slack remaining; zero or negative means the deepest object the caller’s layout can write already meets or exceeds the legacyMAX_PATHceiling from this root. Platform-independent by design: the margin is informative on every OS, but only Windows without the long-path opt-in enforces the ceiling it measures against.The worst-case suffix is a required argument rather than a constant of this module. It was previously a literal here, spelling one hand-picked
<namespace>sample into the arithmetic becausecoresits below the layers that own the on-disk grammar. That sample was drawn from the wrong vocabulary entirely: the<namespace>segment of a provider object path is a registered secure-object storage namespace, and the literal named a bucket-event object type, none of whose values is a registered namespace. The real longest namespace is materially longer, so the resulting ceiling understated every margin this function returned. Taking the budget from the caller lets the owning layer derive it from its own registry — structurally, over the whole domain — without inverting the dependency graph. Seewindows_worst_case_object_path_suffix_length().- Parameters:
root (
Path) – The candidate storage root (cadrumo_local_storage_rootor an outbound-storage provider root).object_path_suffix_length (
int) – Length of the deepest path suffix (leading separator through file extension) the caller’s layout can append belowroot.
- Return type:
- Returns:
The signed character margin described above.
- Raises:
CoreValidationError – When
object_path_suffix_lengthis not a positive length, which would silently inflate the margin.
- resolve_project_path(value, *, state_root_inputs=None)[source]¶
Resolve a relative path against the application-data anchor.
Absolute paths are returned as absolute resolved paths. Relative paths are interpreted against
_relative_path_anchor()— never the process cwd, which keeps config defaults stable regardless of where the CLI process starts, and never a bare checkout-root walk, which keeps an installed run’s relative override out of a virtualenv or ephemeral package cache. This helper is not a containment guard.- Parameters:
value (
str|Path) – An absolute or relative path; user-style~references are expanded.state_root_inputs (
StateRootInputs|None) – Optional injectableStateRootInputsseam forwarded to_relative_path_anchor().None(the default) captures the live process’s inputs.
- Return type:
Path- Returns:
The fully resolved absolute
pathlib.Path.
- normalize_project_relative_path(value, *, state_root_inputs=None)[source]¶
Normalise an optional path setting to an absolute, data-root-anchored path.
Used by settings validators for optional path fields. It preserves
Noneand delegates path semantics toresolve_project_path(); it does not verify that the resulting path exists.- Parameters:
value (
Path|None) – Optional configured path, orNone.state_root_inputs (
StateRootInputs|None) – Optional injectableStateRootInputsseam forwarded toresolve_project_path().None(the default) captures the live process’s inputs.
- Return type:
Path|None- Returns:
NonewhenvalueisNone; otherwise the resolved absolute path produced byresolve_project_path().
- effective_storage_root(root=None, *, settings=None, state_root_inputs=None)[source]¶
Return the effective Cadrumo storage root: a caller override, or the settings default.
The single accessor for the “an explicit root override wins, otherwise fall back to
Settings.cadrumo_local_storage_root” fallback that six call sites acrossapplication/user_profileandapplication/config_reset_repository.pyeach re-implemented inline. Comparing the six copies surfaced real drift, not just duplication: one normalised an override by calling bareresolve(), which for a relative override resolves against the process’s current working directory rather than anchoring it the way every other relative operator path in this codebase does; the other four returned an explicit override completely unnormalised — a relative override, or one carrying~, passed straight through un-expanded and un-resolved. Both are defects: a login or profile-repository root must compare identically regardless of the directory the process happened to start from, and an unnormalised override risks a cross-platform identity mismatch (a differently-cased or non-canonical path failing to compare equal to itself resolved a second time elsewhere).An explicit
rootis therefore always normalised throughresolve_project_path(): an absolute override resolves as-is (including Windows on-disk casing); a relative override anchors under the platform user-data root — one level above the settings default’s ownstorage/— never under the current working directory and never nested under an already-derived storage root.root is Nonefalls back toSettings.cadrumo_local_storage_root, which the settings field validator already normalises identically on load, so no second resolve is spent on the common (no-override) path.- Parameters:
root (
Path|None) – Optional caller-supplied storage-root override.None(the default) resolvesSettings.cadrumo_local_storage_root.settings (
Settings|None) – Optional already-resolvedSettings, read only whenrootisNone, so a caller that already holds aSettingsinstance need not trigger a secondload_settings().None(the default) loads the live settings.state_root_inputs (
StateRootInputs|None) – Optional injectableStateRootInputsseam forwarded toresolve_project_path()whenrootis supplied.None(the default) captures the live process’s inputs.
- Return type:
Path- Returns:
The resolved absolute storage root.
- file_stat_fingerprint(path)[source]¶
Return a cache-key fingerprint triple for a single file.
The triple
(name, size_bytes, mtime_ns)is a stable, low-cost proxy for file identity used by file-backed loader caches. Any in-place modification that changes size or mtime invalidates the cache without requiring a full content hash.This is not an integrity hash or evidence digest. For byte-level verification use
cadrumo.core.hashing.hash_file()orcadrumo.core.hashing.sha256_file().
- path_stat_fingerprint(path)[source]¶
Return a cache-key fingerprint triple for a single file, keyed by its full path.
The path-keyed sibling of
file_stat_fingerprint(). That function’s triple starts with the bare file name, which is correct only when every candidate is known to come from the same directory (a tree-walk fingerprint); reused as a global cache key spanning multiple directories, a name-only first element collides across unrelated files that happen to share a filename. This triple starts withstr(path)instead, so it stays unique across directories. Usefile_stat_fingerprint()for a same-directory tree fingerprint (its shorter name-only key is the correct, and cheaper, choice there); use this one for a single-file loader cache keyed on a resolved, possibly cross-directory path.
- directory_byte_total(directory, *, tolerate_errors=False, entries=None)[source]¶
Return
(total_bytes, file_count)for every regular file underdirectory.A missing (or not-a-directory)
directoryreports(0, 0)rather than raising, mirroring the shape every prior hand-rolled walker used.- Parameters:
directory (
Path) – The directory to sum. Only used to short-circuit on absence whenentriesis not supplied; the actual walk readsentries(or a fresh recursive scan ofdirectory).tolerate_errors (
bool) –When
False(the default), anOSErrorraised while statting a candidate propagates, as does one raised while advancing a caller-suppliedentriesiterator. WhenTrue, the failing candidate — or the rest of an interrupted walk — is skipped and the total reflects only what was successfully stat’d, so a blob write racing this read never crashes the caller.The flag does not reach the internal scan’s own descent: an unreadable subdirectory is skipped silently whatever it is set to, the scan reporting what it could read rather than raising from beneath the root. That predates the move off
Path.rglob, which swallowed the same errors — so an injectedentriesis the only walk advance this flag governs.entries (
Iterable[Path] |None) – Pre-enumerated candidates to sum instead of a fresh recursive scan ofdirectory. Injectable so a caller that already holds an enumeration can reuse it, and so a test can reproduce a file vanishing mid-walk deterministically (list the candidate, delete it on disk, then call this function with that stale reference).
- Return type:
- Returns:
(total_bytes, file_count)summed over every regular file encountered. Directories and other non-regular entries among the candidates are silently skipped (not an error).
- select_filesystem_retention_survivors(entries, *, timestamp, cutoff=None, max_count=None, max_total_bytes=None, size_fn=None, combine='sequential', protect_newest=0)[source]¶
Select survivors/removals under composable age, count, and byte bounds.
Mirrors
select_retention_removal_keys()’s pure rank-and-bound shape, generalized to a filesystem entry (a run directory, a dump file, a telemetry file, a compiled-cache pickle) instead of a secure-object key, and widened from that primitive’s fixed cutoff-then-count pipeline to composable, independently-optional bounds. Deletion stays with the caller: this function only decides who survives.Entries are ranked newest-first by
timestamp(a stable sort, so entries sharing a timestamp keep their relative input order — pass entries pre-sorted by a secondary key when that tie-break matters, e.g. filename).protect_newestexempts the top-ranked N entries from removal under every bound, while still counting them toward themax_total_bytesrunning total and toward rank positions incombine="union"mode.- Parameters:
entries (
Sequence[TypeVar(EntryT)]) – Candidates to rank and select over. Order does not matter; the function re-ranks internally.timestamp (
Callable[[TypeVar(EntryT)],TypeVar(TimestampT, bound=_RetentionTimestamp)]) – Projection returning each entry’s retention timestamp.cutoff (
Optional[TypeVar(TimestampT, bound=_RetentionTimestamp)]) – Age boundary. An entry strictly older thancutoffis a removal candidate; an entry exactly atcutoffsurvives.max_count (
int|None) – Maximum surviving entries under the rank/count bound.max_total_bytes (
int|None) – Total-size ceiling; requiressize_fn. Oldest surviving entries are dropped until the running total fits.size_fn (
Callable[[TypeVar(EntryT)],int] |None) – Per-entry byte size, required whenmax_total_bytesis set.combine (
Literal['sequential','union']) –"sequential"(default) appliescutoff, thenmax_count, thenmax_total_bytesas successive stages, each narrowing the previous stage’s survivors — mirrorsselect_retention_removal_keys()’s cutoff-then-count shape, extended with a byte-total stage."union"evaluatescutoffandmax_countindependently against the full (unstaged) ranking and removes an entry matching either — the shape a “keep the newest N sessions, otherwise prune by age or rank” policy needs, where a rank-3 entry within the age window still gets removed for being beyond the count bound."union"does not supportmax_total_bytes(a byte ceiling has no rank-independent per-entry membership test).protect_newest (
int) – The top-ranked N entries survive unconditionally (still counted towardmax_total_bytesand toward rank positions in"union"mode).
- Return type:
- Returns:
(keep, remove), both drawn fromentrieswith no entry in both.- Raises:
ValueError – When no bound is supplied, when
max_total_bytesis supplied withoutsize_fn, or whencombine="union"is paired withmax_total_bytes.