oc_chat
Loading...
Searching...
No Matches
errors.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oc_chat/registry/source_migrations/errors.py
4
5# OCT-LINT: disable=tests_exist reason="Covered by oc_chat/tests/registry/test_source_migrate_runner.py"
6"""
7The migrate refusal exception class (Plan 142m).
8
9Purpose
10-------
11Carry every migrate-specific §8 refusal code on ONE exception type that
12subclasses ``SourceError`` **directly** — never ``SourceScanError`` /
13``SourceValidationError`` — so ``exit_code_for``'s exit-1 short-circuit
14(``isinstance(error, (SourceScanError, SourceValidationError))``, ``envelope.py``)
15does NOT fire, and the ten migrate codes route through the ``code in
16_EXIT_BY_CODE`` branch to **exit 2**. A plain ``Exception`` subclass would escape
17the pinned ``(SourceError, RegenerateError)`` handler catch as an uncaught
18traceback, so ``SourceError`` is the load-bearing base.
19
20Beyond the base ``code``/``records``/``hint`` the class carries the migrate
21recovery/hash channel the handler passes EXPLICITLY to ``build_envelope`` (the
22recovery keys are never ``getattr``-read inside ``build_envelope``): the per-phase
23``raw_source_tree_hash``/``source_hash`` basis (the failure-stage table) and the
24post-write ``rollback``/``journal_path``/``remaining_paths``/``journal_unresolved``
25fields.
26
27Responsibilities
28----------------
29- Define ``MigrateError`` carrying the §8 code + the per-phase hash basis + the
30 post-write recovery channel.
31
32Contracts
33---------
34- Subclasses ``SourceError`` DIRECTLY (never the two exit-1 leaf classes).
35- Recovery fields are set by the runner and passed EXPLICITLY to ``build_envelope``.
36
37Diagnostics
38-----------
39Domain: OC_CHAT.MAIN
40Levels:
41 L2 - refusal construction lifecycle
42 L3 - refusal code context
43 L4 - module load
44
45Dependencies
46------------
47- oc_diagnostics (structured debug logging via _dbg)
48- oc_chat.registry.source.errors (the SourceError base)
49"""
50from __future__ import annotations
51
52from typing import Optional, Sequence
53
54from oc_diagnostics import _dbg
55
56from oc_chat.registry.source.errors import SourceError
57
58_dbg("OC_CHAT.MAIN", "module", "loaded", 4)
59
60
62 """A ``roadmap source migrate`` refusal (§8) — subclasses ``SourceError``
63 DIRECTLY so ``exit_code_for`` routes its ``SRC_*`` code via ``_EXIT_BY_CODE``
64 to exit 2 (never the SourceScan/SourceValidation exit-1 short-circuit).
65
66 ``raw_source_tree_hash``/``source_hash`` carry the failure-stage hash basis;
67 ``rollback``/``journal_path``/``remaining_paths``/``journal_unresolved`` carry
68 the post-write recovery state — all read by the handler and passed EXPLICITLY
69 to ``build_envelope`` (never getattr'd inside it).
70 """
71
73 self,
74 message: str,
75 *,
76 code: str,
77 records: Optional[Sequence[str]] = None,
78 hint: Optional[str] = None,
79 raw_source_tree_hash: Optional[str] = None,
80 source_hash: Optional[str] = None,
81 rollback: Optional[str] = None,
82 journal_path: Optional[str] = None,
83 remaining_paths: Optional[Sequence[str]] = None,
84 journal_unresolved: Optional[bool] = None,
85 ) -> None:
86 func = "MigrateError.__init__"
87 super().__init__(message, code=code, records=records, hint=hint)
88 self.raw_source_tree_hash = raw_source_tree_hash
89 self.source_hash = source_hash
90 self.rollback = rollback
91 self.journal_path = journal_path
92 self.remaining_paths: Optional[list[str]] = (
93 list(remaining_paths) if remaining_paths is not None else None
94 )
95 self.journal_unresolved = journal_unresolved
96 _dbg("OC_CHAT.MAIN", func, f"migrate refusal {code}", 3)
97
98
99__all__ = ["MigrateError"]
None __init__(self, str message, *, str code, Optional[Sequence[str]] records=None, Optional[str] hint=None, Optional[str] raw_source_tree_hash=None, Optional[str] source_hash=None, Optional[str] rollback=None, Optional[str] journal_path=None, Optional[Sequence[str]] remaining_paths=None, Optional[bool] journal_unresolved=None)
Definition errors.py:85