From d15f02f9b44ae7a541e54042b297fdd434bdb305 Mon Sep 17 00:00:00 2001 From: Oscar Levin Date: Sat, 8 Aug 2026 09:16:14 -0400 Subject: [PATCH 1/4] Improve exit logging --- pretext/__init__.py | 2 +- pretext/cli.py | 14 +++++++------- pretext/logger.py | 17 ++++++++++++++++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pretext/__init__.py b/pretext/__init__.py index 94e6cfd7..eddd1600 100644 --- a/pretext/__init__.py +++ b/pretext/__init__.py @@ -18,7 +18,7 @@ VERSION = get_version("pretext", Path(__file__).parent.parent) -CORE_COMMIT = "35f8d01149b0f657cd6fbd8d67865e0f6d487560" +CORE_COMMIT = "6f1b557cb7aed2b86eb40187c21b56009f97ea1b" def activate() -> None: diff --git a/pretext/cli.py b/pretext/cli.py index 6866d39c..e3d68ef3 100644 --- a/pretext/cli.py +++ b/pretext/cli.py @@ -636,7 +636,7 @@ def build( except AssertionError as e: log.warning("Assertion error in getting target.") utils.show_target_hints(target_name, project, task="build") - log.critical("Exiting without completing build.") + log.exit("Exiting without completing build.") log.debug(e, exc_info=True) return @@ -716,7 +716,7 @@ def build( "It appears there is an error with your project.ptx or publication file. See the details below." ) log.critical(e) - log.critical("Failed to build without errors. Exiting...") + log.exit("Failed to build without errors. Exiting...") log.debug( "\n------------------------\nException info:\n------------------------\n", exc_info=True, @@ -726,7 +726,7 @@ def build( log.critical(e) log.debug("Exception info:\n------------------------\n", exc_info=True) log.info("------------------------") - log.critical("Failed to build without errors. Exiting...") + log.exit("Failed to build without errors. Exiting...") return @@ -911,7 +911,7 @@ def generate( target = project.get_target(name=target_name) except AssertionError as e: utils.show_target_hints(target_name, project, task="generating assets for") - log.critical("Exiting without completing build.") + log.exit("Exiting without completing build.") log.debug(e, exc_info=True) return @@ -940,7 +940,7 @@ def generate( "It appears there is an error with your project.ptx or publication file. See the details below." ) log.critical(e) - log.critical("Failed to build. Exiting...") + log.exit("Failed to build. Exiting...") log.debug( "\n------------------------\nException info:\n------------------------\n", exc_info=True, @@ -950,7 +950,7 @@ def generate( log.critical(e) log.debug("Exception info:\n------------------------\n", exc_info=True) log.info("------------------------") - log.critical("Generating assets as failed. Exiting...") + log.exit("Generating assets as failed. Exiting...") return @@ -1075,7 +1075,7 @@ def view( target = project.get_target(name=target_name, log_info_for_none=not stage) except AssertionError as e: utils.show_target_hints(target_name, project, task="view") - log.critical("Exiting.") + log.exit("Exiting.") log.debug(e, exc_info=True) return diff --git a/pretext/logger.py b/pretext/logger.py index 4ed95ce0..bbe9f206 100644 --- a/pretext/logger.py +++ b/pretext/logger.py @@ -7,12 +7,26 @@ log = logging.getLogger("ptxlogger") +# EXIT is CLI-only: the wrap-up line a command logs right before handing off +# to `exit_command` (e.g. "Failed to build without errors. Exiting..."). It +# announces that the run is stopping; it isn't itself an error, so CRITICAL +# was too strong, and keeping it below ERROR keeps it out of +# error_flush_handler's buffer, so it isn't repeated in the flushed report. +EXIT_LEVEL = 35 +logging.addLevelName(EXIT_LEVEL, "exit") + +def _log_exit(message, *args, **kwargs): + if log.isEnabledFor(EXIT_LEVEL): + log._log(EXIT_LEVEL, message, args, **kwargs) + +log.exit = _log_exit class ColorFormatter(click_log.ColorFormatter): """click_log prefixes a message with its level name, but only for the levels in its own `colors` table; an unrecognized level gets no label at all. Core PreTeXt renames level 50 to FATAL and adds BUG (45) and FALLBACK (25), so - those messages arrived unlabeled. Extend the table to cover them. + those messages arrived unlabeled. Extend the table to cover them, along + with the CLI's own EXIT level above. """ colors = { @@ -20,6 +34,7 @@ class ColorFormatter(click_log.ColorFormatter): "fatal": dict(fg="red", bold=True), "bug": dict(fg="magenta"), "fallback": dict(fg="cyan"), + "exit": dict(fg="red"), } From bbd3d76f59e82225cc0e10af6ad4cc27991ee620 Mon Sep 17 00:00:00 2001 From: Oscar Levin Date: Sat, 8 Aug 2026 09:17:39 -0400 Subject: [PATCH 2/4] format --- pretext/logger.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pretext/logger.py b/pretext/logger.py index bbe9f206..9bf6f626 100644 --- a/pretext/logger.py +++ b/pretext/logger.py @@ -15,12 +15,15 @@ EXIT_LEVEL = 35 logging.addLevelName(EXIT_LEVEL, "exit") + def _log_exit(message, *args, **kwargs): if log.isEnabledFor(EXIT_LEVEL): log._log(EXIT_LEVEL, message, args, **kwargs) + log.exit = _log_exit + class ColorFormatter(click_log.ColorFormatter): """click_log prefixes a message with its level name, but only for the levels in its own `colors` table; an unrecognized level gets no label at all. Core From a550475803652e559da86629b70813fcf4b8f0a9 Mon Sep 17 00:00:00 2001 From: Oscar Levin Date: Sat, 8 Aug 2026 09:23:58 -0400 Subject: [PATCH 3/4] update changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eca15343..378634d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,17 @@ Instructions: Add a subsection under `[Unreleased]` for additions, fixes, change ## [Unreleased] +### Added + +- Improvements to Fill-In-The-Blank questions. +- Headnote element. +- Support for gdscript activecode. + +### Changed + +- WeBWorK javascript is no longer versioned. + + ## [2.47.1] - 2026-08-04 Includes updates to core through commit: [35f8d01](https://github.com/PreTeXtBook/pretext/commit/35f8d01149b0f657cd6fbd8d67865e0f6d487560) From bd27216f5d0f608318fbc70d59d5e27cc0a7327e Mon Sep 17 00:00:00 2001 From: Oscar Levin Date: Sat, 8 Aug 2026 09:42:19 -0400 Subject: [PATCH 4/4] fix up logger mess --- pretext/cli.py | 4 +--- pretext/logger.py | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pretext/cli.py b/pretext/cli.py index e3d68ef3..1034e080 100644 --- a/pretext/cli.py +++ b/pretext/cli.py @@ -1,7 +1,5 @@ import importlib import importlib.util -import logging -import logging.handlers import sys import click import click_log @@ -32,7 +30,7 @@ from .project import Project -log = logging.getLogger("ptxlogger") +log = logger.log logger.add_log_stream_handler() error_flush_handler = logger.get_log_error_flush_handler() diff --git a/pretext/logger.py b/pretext/logger.py index 9bf6f626..14912613 100644 --- a/pretext/logger.py +++ b/pretext/logger.py @@ -3,10 +3,9 @@ import sys import logging import logging.handlers +from typing import Any, cast import click_log -log = logging.getLogger("ptxlogger") - # EXIT is CLI-only: the wrap-up line a command logs right before handing off # to `exit_command` (e.g. "Failed to build without errors. Exiting..."). It # announces that the run is stopping; it isn't itself an error, so CRITICAL @@ -16,12 +15,20 @@ logging.addLevelName(EXIT_LEVEL, "exit") -def _log_exit(message, *args, **kwargs): - if log.isEnabledFor(EXIT_LEVEL): - log._log(EXIT_LEVEL, message, args, **kwargs) +class PretextLogger(logging.Logger): + """A `Logger` that also knows how to log at EXIT_LEVEL.""" + + def exit(self, message: object, *args: Any, **kwargs: Any) -> None: + self.log(EXIT_LEVEL, message, *args, **kwargs) -log.exit = _log_exit +logging.setLoggerClass(PretextLogger) +log = cast(PretextLogger, logging.getLogger("ptxlogger")) +# Modules elsewhere (including core) fetch "ptxlogger" by name; whichever of +# them runs first decides the class. Retag the singleton so `log.exit` exists +# no matter the import order, then restore the default for everyone else. +log.__class__ = PretextLogger +logging.setLoggerClass(logging.Logger) class ColorFormatter(click_log.ColorFormatter):