Option C Tools
Loading...
Searching...
No Matches
octrc.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oct/core/octrc.py
4
5"""
6Purpose
7-------
8OI-519 — single source of truth for ``.octrc.json`` loading. Multiple OCT
9modules (linter, formatter, health, typecheck, MCP config) used to each
10carry their own ``json.loads`` + ``try/except`` around the same file; that
11drift-prone pattern is replaced by :func:`load_octrc` and its silent
12convenience wrapper :func:`load_octrc_safe`.
13
14Responsibilities
15----------------
16- Resolve ``<project_root>/.octrc.json``.
17- Tolerate missing file, OSError, invalid JSON, and non-object top-level
18 documents — never raise.
19- Return a ``(config, errors)`` tuple from :func:`load_octrc` so callers
20 that care about strict-mode handling (the linter) can surface messages.
21- Return a plain ``dict`` from :func:`load_octrc_safe` for callers that
22 only want the data.
23
24Diagnostics
25-----------
26Domain: OCT-CORE
27Levels:
28 (No diagnostics emitted; callers decide how to react to errors.)
29
30Contracts
31---------
32- Both functions accept the project root, not the config path, so behavior
33 stays consistent with every other OCT helper.
34- ``load_octrc_safe(root)`` is exactly equivalent to
35 ``load_octrc(root)[0]``.
36- ``load_octrc`` never raises; errors are always returned as strings.
37
38Dependencies
39------------
40- oct.core.option_c_dir (resolve_octrc path resolver)
41"""
42
43from __future__ import annotations
44
45import json
46from pathlib import Path
47
48from oct.core.option_c_dir import resolve_octrc
49
50
51OCTRC_FILENAME = ".octrc.json"
52
53
54def load_octrc(project_root: Path) -> tuple[dict, list[str]]:
55 """Load the project's OCT RC config (``.octrc.json`` / ``octrc.json``).
56
57 FS-539: prefers ``.option_c/octrc.json`` when present, falls back to
58 the legacy ``<project_root>/.octrc.json`` otherwise. The concrete
59 path is resolved via :func:`oct.core.option_c_dir.resolve_octrc`.
60
61 Returns
62 -------
63 (config, errors) : tuple[dict, list[str]]
64 ``config`` is the parsed JSON object, or an empty dict if the file
65 is missing / unreadable / malformed / not a JSON object.
66 ``errors`` is a list of human-readable error strings; empty when
67 the file was absent or loaded successfully.
68 """
69 octrc_path = resolve_octrc(project_root)
70 label = octrc_path.name # "octrc.json" (modern) or ".octrc.json" (legacy)
71 if not octrc_path.is_file():
72 return {}, []
73
74 try:
75 raw = octrc_path.read_text(encoding="utf-8")
76 except OSError as exc:
77 return {}, [f"{label} unreadable: {exc}"]
78
79 try:
80 data = json.loads(raw)
81 except json.JSONDecodeError as exc:
82 return {}, [f"{label} has invalid JSON: {exc}"]
83
84 if not isinstance(data, dict):
85 return {}, [
86 f"{label} must be a JSON object, got "
87 f"{type(data).__name__}"
88 ]
89
90 return data, []
91
92
93def load_octrc_safe(project_root: Path) -> dict:
94 """Load ``.octrc.json`` quietly, returning ``{}`` on any failure."""
95 data, _errors = load_octrc(project_root)
96 return data
97
98
99__all__ = ["OCTRC_FILENAME", "load_octrc", "load_octrc_safe"]
tuple[dict, list[str]] load_octrc(Path project_root)
Definition octrc.py:54
dict load_octrc_safe(Path project_root)
Definition octrc.py:93