Repository: scikit-build/scikit-build-core (python)

Issue:
Pseudo-rich logger/printer sees colors
"[GCC]" is not a color command, but the logger would like to interpret it as such:

```python
[   40s] ____________________________ test_builder_printout _____________________________
[   40s]
[   40s] capsys = <_pytest.capture.CaptureFixture object at 0x7f704905b700>
[   40s]
[   40s]     def test_builder_printout(capsys):
[   40s] >       main()
[   40s]
[   40s] tests/test_printouts.py:5:
[   40s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[   40s] ../../BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.10/site-packages/scikit_build_core/builder/__main__.py:21: in main
[   40s]     rich_print(f"[bold]Scikit-build-core {__version__}[/bold] on Python {sys.version}")
[   40s] ../../BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.10/site-packages/scikit_build_core/_logging.py:128: in rich_print
[   40s]     args_2 = tuple(_process_rich(arg) for arg in args)
[   40s] ../../BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.10/site-packages/scikit_build_core/_logging.py:128: in <genexpr>
[   40s]     args_2 = tuple(_process_rich(arg) for arg in args)
[   40s] ../../BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.10/site-packages/scikit_build_core/_logging.py:121: in _process_rich
[   40s]     return ANY_ESCAPE.sub(
[   40s] ../../BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.10/site-packages/scikit_build_core/_logging.py:122: in <lambda>
[   40s]     lambda m: "".join(colors()[x] for x in m.group(1).split()),
[   40s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[   40s]
[   40s] .0 = <list_iterator object at 0x7f704905a860>
[   40s]
[   40s] >       lambda m: "".join(colors()[x] for x in m.group(1).split()),
[   40s]         str(msg),
[   40s]     )
[   40s] E   KeyError: 'GCC'
[   40s]
[   40s] ../../BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.10/site-packages/scikit_build_core/_logging.py:122: KeyError
```

```python
abuild@skylab:~> python3 -m scikit_build_core.builder.__main__
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/abuild/rpmbuild/BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.11/site-packages/scikit_build_core/builder/__main__.py", line 39, in <module>
    main()
  File "/home/abuild/rpmbuild/BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.11/site-packages/scikit_build_core/builder/__main__.py", line 21, in main
    rich_print(f"[bold]Scikit-build-core {__version__}[/bold] on Python {sys.version}")
  File "/home/abuild/rpmbuild/BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.11/site-packages/scikit_build_core/_logging.py", line 128, in rich_print
    args_2 = tuple(_process_rich(arg) for arg in args)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/abuild/rpmbuild/BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.11/site-packages/scikit_build_core/_logging.py", line 128, in <genexpr>
    args_2 = tuple(_process_rich(arg) for arg in args)
                   ^^^^^^^^^^^^^^^^^^
  File "/home/abuild/rpmbuild/BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.11/site-packages/scikit_build_core/_logging.py", line 121, in _process_rich
    return ANY_ESCAPE.sub(
           ^^^^^^^^^^^^^^^
  File "/home/abuild/rpmbuild/BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.11/site-packages/scikit_build_core/_logging.py", line 122, in <lambda>
    lambda m: "".join(colors()[x] for x in m.group(1).split()),
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/abuild/rpmbuild/BUILDROOT/python-scikit-build-core-0.9.5-0.x86_64/usr/lib/python3.11/site-packages/scikit_build_core/_logging.py", line 122, in <genexpr>
    lambda m: "".join(colors()[x] for x in m.group(1).split()),
                      ~~~~~~~~^^^
KeyError: 'GCC'
abuild@skylab:~> python3 -c 'import sys; print(sys.version)'
3.11.9 (main, Apr 08 2024, 06:18:15) [GCC]
```
https://github.com/scikit-build/scikit-build-core/blob/7dd64ecea458bfa37d9ddcbc4f770024d3389939/src/scikit_build_core/_logging.py#L82
https://github.com/scikit-build/scikit-build-core/blob/7dd64ecea458bfa37d9ddcbc4f770024d3389939/src/scikit_build_core/_logging.py#L120-L124

Pull request:
Only replace rich if the color is defined
Fixes #763

Only replace the rich tag if the color is defined in `colors()`

![image](https://github.com/scikit-build/scikit-build-core/assets/4623504/c7b86d94-9818-4aed-b376-66509d3d4ee9)


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b222fbc..8cc0450 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,7 +28,7 @@ jobs:
           python-version: "3.12"
       - uses: pre-commit/action@v3.0.1
         with:
-          extra_args: --hook-stage manual --all-files
+          extra_args: --all-files
       - name: Run PyLint
         run: pipx run nox -s pylint -- --output-format=github
       - name: Run nox generator
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index c0a698c..7221a68 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,7 @@
 ci:
   autoupdate_commit_msg: "chore(deps): update pre-commit hooks"
   autofix_commit_msg: "style: pre-commit fixes"
+  autoupdate_schedule: monthly
 
 exclude: ^src/scikit_build_core/_vendor
 
@@ -56,6 +57,7 @@ repos:
         types_or: [yaml, markdown, html, css, scss, javascript, json]
         args: [--prose-wrap=always]
         exclude: "^tests|src/scikit_build_core/resources/scikit-build.schema.json"
+        stages: [manual]
 
   - repo: https://github.com/pre-commit/mirrors-mypy
     rev: v1.10.0
diff --git a/src/scikit_build_core/_logging.py b/src/scikit_build_core/_logging.py
index c449c74..e2351d0 100644
--- a/src/scikit_build_core/_logging.py
+++ b/src/scikit_build_core/_logging.py
@@ -117,9 +117,20 @@ def colors() -> dict[str, str]:
     return _NO_COLORS
 
 
+def _sub_rich(m: re.Match[str]) -> str:
+    """
+    Replace rich-like tags, but only if they are defined in colors.
+    """
+    color_dict = colors()
+    try:
+        return "".join(color_dict[x] for x in m.group(1).split())
+    except KeyError:
+        return m.group(0)
+
+
 def _process_rich(msg: object) -> str:
     return ANY_ESCAPE.sub(
-        lambda m: "".join(colors()[x] for x in m.group(1).split()),
+        _sub_rich,
         str(msg),
     )
 


Test patch:
diff --git a/tests/test_logging.py b/tests/test_logging.py
index ae99850..6fd4828 100644
--- a/tests/test_logging.py
+++ b/tests/test_logging.py
@@ -20,3 +20,13 @@ def test_rich_print_forcecolor(capsys, monkeypatch):
     monkeypatch.delenv("NO_COLOR", raising=False)
     rich_print("[red bold]hello[/bold] world", end="")
     assert capsys.readouterr().out == "\33[91m\33[1mhello\33[22m world\33[0m"
+
+
+def test_rich_notrichbrackets(capsys, monkeypatch):
+    monkeypatch.setenv("FORCE_COLOR", "1")
+    monkeypatch.delenv("NO_COLOR", raising=False)
+    rich_print("[red bold]hello[/bold] world[/red] [notrich]", end="")
+    assert (
+        capsys.readouterr().out
+        == "\33[91m\33[1mhello\33[22m world\33[0m [notrich]\33[0m"
+    )