From dfae0b3e9f7f999cdf4a2f8e5e85370e0c36f946 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Fri, 31 Oct 2025 14:06:18 +0100 Subject: [PATCH 1/2] Migrate to new plugin system, and only import multiprocessing if needed --- .../plugin.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 25404fa..659fda5 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -9,7 +9,6 @@ import re import os import time -import multiprocessing from pathlib import Path from mkdocs import __version__ as mkdocs_version @@ -59,6 +58,20 @@ def __init__(self): self.last_revision_commits = {} self.created_commits = {} + def on_startup(self, *, command: str, dirty: bool) -> None: + """ + Run on startup. + + Note that "The presence of an on_startup method (even if empty) + migrates the plugin to the new system where the plugin object is + kept across builds within one mkdocs serve." + + Args: + command (str): The mkdocs command being run. + dirty (bool): Whether the build is dirty. + """ + pass + def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: """ Determine which locale to use. @@ -153,6 +166,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: return config def parallel_compute_commit_timestamps(self, files, original_source: Optional[Dict] = None, is_first_commit=False): + import multiprocessing pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count())) results = [] for f in files: From 0c45301fed5f4c3e17c41d06cc8fa1dfcda111e5 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Fri, 31 Oct 2025 14:42:08 +0100 Subject: [PATCH 2/2] Improve cache usage --- .../plugin.py | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 659fda5..985a0a6 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -57,6 +57,7 @@ def __init__(self): super().__init__() self.last_revision_commits = {} self.created_commits = {} + self.is_serve_dirty_build = False def on_startup(self, *, command: str, dirty: bool) -> None: """ @@ -70,7 +71,15 @@ def on_startup(self, *, command: str, dirty: bool) -> None: command (str): The mkdocs command being run. dirty (bool): Whether the build is dirty. """ - pass + # Track if this is an incremental rebuild during mkdocs serve + # dirty=True means it's a rebuild triggered by file changes + # dirty=False means it's a clean/initial build + self.is_serve_dirty_build = dirty + + # Clear cache on clean builds to ensure fresh data + if not dirty: + self.last_revision_commits = {} + self.created_commits = {} def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: """ @@ -179,7 +188,9 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di elif exclude(f.src_path, self.config.get("exclude", [])): continue else: + temp_abs_src_path = str(Path(f.abs_src_path).absolute()) abs_src_path = f.abs_src_path + # Support plugins like monorepo that might have moved the files from the original source that is under git if original_source and abs_src_path in original_source: abs_src_path = original_source[abs_src_path] @@ -187,7 +198,10 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di assert Path(abs_src_path).exists() abs_src_path = str(Path(abs_src_path).absolute()) result = pool.apply_async(self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit)) + # Store both the original path and temp path (if different) so cache lookups work either way results.append((abs_src_path, result)) + if temp_abs_src_path != abs_src_path: + results.append((temp_abs_src_path, result)) pool.close() pool.join() if is_first_commit: @@ -204,6 +218,13 @@ def on_files(self, files: Files, config: MkDocsConfig): if not self.config.get("enabled") or not self.config.get("enable_parallel_processing"): return + # Skip parallel processing on incremental rebuilds (dirty builds during mkdocs serve) + # This avoids the overhead of creating a new multiprocessing pool on every file save + # The cache from the initial build will be reused + if self.is_serve_dirty_build: + logging.debug("[git-revision-date-localized] Skipping parallel processing on incremental rebuild, using cache") + return + # Support monorepo/techdocs, which copies the docs_dir to a temporary directory mono_repo_plugin = config.get("plugins", {}).get("monorepo", None) if mono_repo_plugin is not None and hasattr(mono_repo_plugin, "merger") and mono_repo_plugin.merger is not None: @@ -275,10 +296,18 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con if getattr(page.file, "generated_by", None): last_revision_hash, last_revision_timestamp = "", int(time.time()) else: - last_revision_hash, last_revision_timestamp = self.last_revision_commits.get( - str(Path(page.file.abs_src_path).absolute()), (None, None) - ) - if last_revision_timestamp is None: + # Use cached results if parallel processing is enabled and cache is populated + if self.config.get("enable_parallel_processing") and self.last_revision_commits: + last_revision_hash, last_revision_timestamp = self.last_revision_commits.get( + str(Path(page.file.abs_src_path).absolute()), (None, None) + ) + if last_revision_timestamp is None: + last_revision_hash, last_revision_timestamp = self.util.get_git_commit_timestamp( + path=page.file.abs_src_path, + is_first_commit=False, + ) + else: + # Directly call git if parallel processing is disabled or cache is empty last_revision_hash, last_revision_timestamp = self.util.get_git_commit_timestamp( path=page.file.abs_src_path, is_first_commit=False, @@ -351,10 +380,18 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con if getattr(page.file, "generated_by", None): first_revision_hash, first_revision_timestamp = "", int(time.time()) else: - first_revision_hash, first_revision_timestamp = self.created_commits.get( - str(Path(page.file.abs_src_path).absolute()), (None, None) - ) - if first_revision_timestamp is None: + # Use cached results if parallel processing is enabled and cache is populated + if self.config.get("enable_creation_date") and self.config.get("enable_parallel_processing") and self.created_commits: + first_revision_hash, first_revision_timestamp = self.created_commits.get( + str(Path(page.file.abs_src_path).absolute()), (None, None) + ) + if first_revision_timestamp is None: + first_revision_hash, first_revision_timestamp = self.util.get_git_commit_timestamp( + path=page.file.abs_src_path, + is_first_commit=True, + ) + else: + # Directly call git if parallel processing is disabled or cache is empty first_revision_hash, first_revision_timestamp = self.util.get_git_commit_timestamp( path=page.file.abs_src_path, is_first_commit=True,