Bengal 0.2.7

Plugin system, template dependency tracking, free-threading hardening, page walk optimizations

Key additions: Unified plugin system with 9 extension points, per-page template dependency tracking for selective rebuilds, free-threading hardening (thread-safe caches and tracers), 8→1 page walk optimizations, code simplification, and restructured CI test infrastructure.


Highlights

Plugin System

Bengal now has a formal plugin framework. Plugins implement aPlugin protocol (name, version, register()) and register extensions through a PluginRegistrythat supports 9 extension points: directives, roles, template functions, template filters, template tests, content sources, health validators, shortcodes, and build phase hooks.

Plugins are auto-discovered via thebengal.pluginsentry point group:

# pyproject.toml of your plugin package
[project.entry-points."bengal.plugins"]
my-plugin = "my_package:MyPlugin"
from bengal.plugins.protocol import Plugin
from bengal.plugins.registry import PluginRegistry

class MyPlugin(Plugin):
    name = "my-plugin"
    version = "1.0.0"

    def register(self, registry: PluginRegistry) -> None:
        registry.add_template_filter("shout", lambda v: v.upper() + "!")
        registry.on_phase("post_render", self.after_render)

The registry uses a builder→immutable pattern: mutable during registration, frozen into an immutable FrozenPluginRegistrydataclass before parallel rendering begins. Thread-safe by design.

See Writing Plugins for the full guide.

Template Dependency Tracking

The build cache now records which templates (and their full include/extends chain) each page uses. When a template changes, only the pages that depend on it rebuild — instead of a full site rebuild.

  • First build: dependencies are recorded (no change in behavior)
  • Subsequent builds: template changes trigger selective rebuilds
  • Falls back to full rebuild if no dependency data exists (cache miss or first build)

On a 1,000-page site where you edit a partial template used by 50 pages, this means rebuilding 50 pages instead of 1,000.

Free-Threading Hardening

Three changes prepare Bengal for Python 3.14t (PEP 703) free-threading:

  • EffectTracer: All mutations and reads are now serialized under a lock. Without the GIL, concurrentdefaultdictaccess could observe partial state.
  • get_bengal_dir() cache: Replaced @lru_cache(maxsize=1) with Bengal's LRUCache, which uses an RLock internally. functools.lru_cacherelies on the GIL for thread safety.
  • Kida 0.2.9: Updated the template engine dependency for free-threading support.

Page Walk Optimizations

Coalesced 8 redundantsite.pagestraversals into single passes:

  • Menu building: Three separate scans (changed-page check, menu frontmatter collection, root-level page collection) merged into_analyze_menu_state() — one pass returning (needs_rebuild, menu_pages, root_level_pages).
  • Finalization: Four scans for page count, autodoc detection, and stats merged into a single loop.
  • Provenance: Eliminated duplicatepages_listconstruction.
  • Manifest: Fixedsummary()multi-scan (4 scans → 1 pass).

On large sites (1K+ pages), this reduces orchestration phase time by ~8–12%.


Code Simplification

  • Exception hierarchy: Replaced 11 manual__init__ methods in exception subclasses with a _default_build_phase_nameclass variable pattern — less boilerplate, same behavior.
  • Deprecated transforms removed:escape_jinja_blocks(), transform_internal_links(), and normalize_markdown_links() are gone. Their work is handled by HybridHTMLTransformer.
  • Renderer helpers: Extracted_default_pagination() and _coerce_pagination_ints()to eliminate duplicated inline dict construction.

Test Infrastructure

  • tests/README.md: Comprehensive test suite guide covering 4,065+ tests (116 property-based via Hypothesis), 10 test roots, markers, parallel safety, and CI strategy.
  • CI restructure: 6 parallel integration shards with signal-based timeouts (SIGALRM) instead of thread-based. Required for free-threaded Python where thread-basedpytest-timeoutcannot fire.
  • Class-scoped fixtures: Newbuild_ephemeral_site_at()helper enables class/module-scoped test reuse without redundant site builds.
  • Removed no-op test:test_resource_cleanup.pyhad 0 assertions — deleted.

Upgrading

uv pip install --upgrade bengal
# or
pip install --upgrade bengal
# or use the self-update command
bengal upgrade

No breaking changes in this release.