From 4295ac31327d0c3639ec41a7b55617931c2d44f0 Mon Sep 17 00:00:00 2001 From: Matthias Schoettle Date: Wed, 30 Oct 2024 09:52:45 -0400 Subject: [PATCH 01/44] Add datetime string to span element as title --- src/mkdocs_git_revision_date_localized_plugin/util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index 275f6b5..6287209 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -186,9 +186,10 @@ def add_spans(date_formats: Dict[str, str]) -> Dict[str, str]: """ Wraps the date string in elements with CSS identifiers. """ + datetime_string = date_formats['datetime'] for date_type, date_string in date_formats.items(): date_formats[date_type] = ( - '%s' - % (date_type, date_string) + '%s' + % (date_type, datetime_string, date_string) ) return date_formats From cb7f046e1e521ec2fb88f5ebcc349cf11e629ac0 Mon Sep 17 00:00:00 2001 From: Leonard Sheng Sheng Lee Date: Fri, 29 Nov 2024 09:33:08 +0100 Subject: [PATCH 02/44] fix: broken link Signed-off-by: Leonard Sheng Sheng Lee --- src/mkdocs_git_revision_date_localized_plugin/ci.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/ci.py b/src/mkdocs_git_revision_date_localized_plugin/ci.py index 73df3a0..e203568 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/ci.py +++ b/src/mkdocs_git_revision_date_localized_plugin/ci.py @@ -43,8 +43,8 @@ def raise_ci_warnings(repo) -> None: [git-revision-date-localized-plugin] Running on GitHub Actions might lead to wrong Git revision dates due to a shallow git fetch depth. - Try setting fetch-depth to 0 in your GitHub Action - (see https://github.com/actions/checkout). + Try setting `fetch-depth: 0` in your GitHub Action. + See https://github.com/actions/checkout for more information. """ ) From e777f783a0a192c2ed7ebadaebbd3be289b89e66 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Sat, 22 Feb 2025 15:27:07 +0100 Subject: [PATCH 03/44] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 624b9bf..e0a169a 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,11 @@ (*Example when used together with the [mkdocs-material](https://github.com/squidfunk/mkdocs-material) theme*) -Other MkDocs plugins that use information from git: +Other related MkDocs plugins: - [mkdocs-git-authors-plugin](https://github.com/timvink/mkdocs-git-authors-plugin) for displaying the authors from git - [mkdocs-git-committers-plugin](https://github.com/byrnereese/mkdocs-git-committers-plugin) for displaying authors' github user profiles +- [mkdocs-document-dates](https://github.com/jaywhj/mkdocs-document-dates) for displaying dates based on file creation and modification dates. ## Setup From db89b2043a3d8f78515e95ae966b5013113aeffa Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 10:23:42 +0000 Subject: [PATCH 04/44] fix merge --- docs/options.md | 16 +++++++ .../plugin.py | 1 + .../util.py | 38 +++++++++++++++- .../basic_project/mkdocs_ignored_commits.yml | 8 ++++ tests/test_builds.py | 45 +++++++++++++++++-- tests/test_parse_git_ignore_revs.py | 16 +++++++ 6 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/basic_project/mkdocs_ignored_commits.yml create mode 100644 tests/test_parse_git_ignore_revs.py diff --git a/docs/options.md b/docs/options.md index 1c24090..7580497 100644 --- a/docs/options.md +++ b/docs/options.md @@ -18,6 +18,7 @@ You can customize the plugin by setting options in `mkdocs.yml`. For example: enable_git_follow: true enabled: true strict: true + ignored_commits_file: .git-blame-ignore-revs ``` ## `type` @@ -155,3 +156,18 @@ Default is `true`. When enabled, the logs will show warnings when something is w - git-revision-date-localized: strict: true ``` + +## `ignored_commits_file` + +Default is `None`. When specified, contains a file that contains a list of commit hashes to ignore +when determining the most recent updated time. The format of the file is the same as the format of +git `blame.ignoreRevsFile`. This can be useful to ignore formatting updates or other mass changes to the documents. + + +=== ":octicons-file-code-16: mkdocs.yml" + + ```yaml + plugins: + - git-revision-date-localized: + ignored_commits_file: .git-blame-ignore-revs + ``` diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 4b7fef2..6c302b0 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -44,6 +44,7 @@ class GitRevisionDateLocalizedPlugin(BasePlugin): ("enabled", config_options.Type(bool, default=True)), ("strict", config_options.Type(bool, default=True)), ("enable_git_follow", config_options.Type(bool, default=True)) + ("ignored_commits_file", config_options.Type(str, default=None)), ) def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index 6287209..ca46316 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -16,7 +16,7 @@ NoSuchPathError, ) -from typing import Dict +from typing import Any, Dict, List logger = logging.getLogger("mkdocs.plugins") @@ -32,6 +32,9 @@ def __init__(self, config={}): self.config = config self.repo_cache = {} + ignore_commits_file = self.config.get("ignored_commits_file") + self.ignored_commits = self.parse_git_ignore_revs(ignore_commits_file) if ignore_commits_file else [] + def _get_repo(self, path: str) -> Git: if not os.path.isdir(path): path = os.path.dirname(path) @@ -82,6 +85,7 @@ def get_git_commit_timestamp( follow_option=self.config.get('enable_git_follow') + # Ignored commits are only considered for the most recent update, not for creation if is_first_commit: # diff_filter="A" will select the commit that created the file commit_timestamp = git.log( @@ -100,6 +104,22 @@ def get_git_commit_timestamp( ignore_all_space=True, ignore_blank_lines=True ) + # Retrieve the history for the file in the format + # The maximum number of commits we will ever need to examine is 1 more than the number of ignored commits. + lines = git.log( + realpath, date="unix", format="%H %at", n=len(self.ignored_commits)+1, no_show_signature=True, + ).split("\n") + + # process the commits for the file in reverse-chronological order. Ignore any commit that is on the + # ignored list. If the line is empty, we've reached the end and need to use the fallback behavior + for line in lines: + if not line: + commit_timestamp = "" + break + commit, commit_timestamp = line.split(" ") + if not any(commit.startswith(x) for x in self.ignored_commits): + break + except (InvalidGitRepositoryError, NoSuchPathError) as err: if self.config.get('fallback_to_build_date'): log( @@ -193,3 +213,19 @@ def add_spans(date_formats: Dict[str, str]) -> Dict[str, str]: % (date_type, datetime_string, date_string) ) return date_formats + + @staticmethod + def parse_git_ignore_revs(filename: str) -> List[str]: + """ + Parses a file that is the same format as git's blame.ignoreRevsFile and return the list of commit hashes. + + Whitespace, blanklines and comments starting with # are all ignored. + """ + result = [] + with open(filename, "rt") as f: + for line in f: + line = line.split("#", 1)[0].strip() + if not line: + continue + result.append(line) + return result diff --git a/tests/fixtures/basic_project/mkdocs_ignored_commits.yml b/tests/fixtures/basic_project/mkdocs_ignored_commits.yml new file mode 100644 index 0000000..6378e33 --- /dev/null +++ b/tests/fixtures/basic_project/mkdocs_ignored_commits.yml @@ -0,0 +1,8 @@ +site_name: test gitrevisiondatelocalized_plugin +use_directory_urls: true + +plugins: + - search + - git-revision-date-localized: + enable_creation_date: True + ignored_commits_file: ignored-commits.txt diff --git a/tests/test_builds.py b/tests/test_builds.py index ef4c851..9d5a6ca 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -160,10 +160,15 @@ def setup_commit_history(testproject_path): repo.git.commit(message="add homepage", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000 file_name = os.path.join(testproject_path, "docs/page_with_tag.md") + with open(file_name, "a") as the_file: + the_file.write("test\n") + repo.git.add("docs/page_with_tag.md") + repo.git.commit(message="update homepage #1", author=author, date="1525475836") # Fri May 04 2018 23:17:16 GMT+0000 + with open(file_name, "a") as the_file: the_file.write("awa\n") repo.git.add("docs/page_with_tag.md") - repo.git.commit(message="update homepage", author=author, date="1642911026") # Sun Jan 23 2022 04:10:26 GMT+0000 + repo.git.commit(message="update homepage #2", author=author, date="1642911026") # Sun Jan 23 2022 04:10:26 GMT+0000 if os.path.exists("docs/page_with_renamed.md"): bf_file_name = os.path.join(testproject_path, "docs/page_with_renamed.md") @@ -373,10 +378,10 @@ def test_tags_are_replaced(tmp_path, mkdocs_file): pytest.skip("Not necessary to test the JS library") # Make sure count_commits() works - # We created 10 commits in setup_commit_history() + # We created 11 commits in setup_commit_history() with working_directory(testproject_path): u = Util() - assert commit_count(u._get_repo("docs/page_with_tag.md")) == 10 + assert commit_count(u._get_repo("docs/page_with_tag.md")) == 11 # the revision date was in 'setup_commit_history' was set to 1642911026 (Sun Jan 23 2022 04:10:26 GMT+0000) @@ -667,3 +672,37 @@ def test_mkdocs_genfiles_plugin(tmp_path): validate_build( testproject_path, plugin_config ) + + +def test_ignored_commits(tmp_path): + testproject_path = setup_clean_mkdocs_folder( + "tests/fixtures/basic_project/mkdocs_ignored_commits.yml", tmp_path + ) + repo = setup_commit_history(testproject_path) + + # First test that the middle commit doesn't show up by default + # January 23, 2022 is the date of the most recent commit + with open(str(testproject_path / "ignored-commits.txt"), "wt") as fp: + fp.write("") + + result = build_docs_setup(testproject_path) + assert result.exit_code == 0 + + page_with_tag = testproject_path / "site/page_with_tag/index.html" + contents = page_with_tag.read_text(encoding="utf8") + assert "January 23, 2022" in contents + + # Now mark the most recent change to page_with_tag as ignored + # May 4, 2018 is the date of the second most recent commit + hash = repo.git.log("docs/page_with_tag.md", format="%H", n=1) + + with open(str(testproject_path / "ignored-commits.txt"), "wt") as fp: + fp.write(hash) + + # should not raise warning + result = build_docs_setup(testproject_path) + assert result.exit_code == 0 + + page_with_tag = testproject_path / "site/page_with_tag/index.html" + contents = page_with_tag.read_text(encoding="utf8") + assert "May 4, 2018" in contents \ No newline at end of file diff --git a/tests/test_parse_git_ignore_revs.py b/tests/test_parse_git_ignore_revs.py new file mode 100644 index 0000000..93cf7fa --- /dev/null +++ b/tests/test_parse_git_ignore_revs.py @@ -0,0 +1,16 @@ +from mkdocs_git_revision_date_localized_plugin.util import Util +import pytest +import tempfile + +TEST_PARAMS = [ + (b"abc123\n", ["abc123"]), + (b"abc123 # comments are ignored\n", ["abc123"]), + (b"\n\n\n\n\nabc123\n\n\n\n\n", ["abc123"]), +] + +@pytest.mark.parametrize("test_input,expected", TEST_PARAMS) +def test_parse_git_ignore_revs(test_input, expected): + with tempfile.NamedTemporaryFile() as fp: + fp.write(test_input) + fp.flush() + assert Util.parse_git_ignore_revs(fp.name) == expected From 912665053cdfdc48c4de76668ce2da81ad55c3cf Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 10:34:49 +0000 Subject: [PATCH 05/44] fix typo --- CONTRIBUTING.md | 2 +- src/mkdocs_git_revision_date_localized_plugin/plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f10cde8..21ed37c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Thanks for considering to contribute to this project! Some guidelines: Make sure to install an editable version before running the tests: ```python -pip install -r requirement_dev.txt +pip install -r requirements_dev.txt pip install -e . pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report term-missing tests/ ``` diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 6c302b0..3349fa6 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -43,7 +43,7 @@ class GitRevisionDateLocalizedPlugin(BasePlugin): ("enable_creation_date", config_options.Type(bool, default=False)), ("enabled", config_options.Type(bool, default=True)), ("strict", config_options.Type(bool, default=True)), - ("enable_git_follow", config_options.Type(bool, default=True)) + ("enable_git_follow", config_options.Type(bool, default=True)), ("ignored_commits_file", config_options.Type(str, default=None)), ) From 4d0ab6911760a6b29ec195f5c49ac04914530bf7 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 12:04:20 +0000 Subject: [PATCH 06/44] fix windows issue --- docs/options.md | 6 ++-- .../plugin.py | 2 +- .../util.py | 35 +++++++++++++------ tests/fixtures/basic_project/.gitignore | 1 + tests/test_builds.py | 12 +++---- 5 files changed, 36 insertions(+), 20 deletions(-) create mode 100644 tests/fixtures/basic_project/.gitignore diff --git a/docs/options.md b/docs/options.md index 7580497..1f00e52 100644 --- a/docs/options.md +++ b/docs/options.md @@ -159,9 +159,9 @@ Default is `true`. When enabled, the logs will show warnings when something is w ## `ignored_commits_file` -Default is `None`. When specified, contains a file that contains a list of commit hashes to ignore -when determining the most recent updated time. The format of the file is the same as the format of -git `blame.ignoreRevsFile`. This can be useful to ignore formatting updates or other mass changes to the documents. +Default is `None`. You can specify a file path (relative to your `mkdocs.yml` directory) that contains a list of commit hashes to ignore +when determining the revision date. The format of the file is the same as the format of +git `blame.ignoreRevsFile`. This can be useful to ignore specific commits that apply formatting updates or other mass changes to the documents. === ":octicons-file-code-16: mkdocs.yml" diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 3349fa6..eea5727 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -67,7 +67,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: assert self.config['type'] in ["date","datetime","iso_date","iso_datetime","timeago","custom"] - self.util = Util(config=self.config) + self.util = Util(config=self.config, mkdocs_dir=os.path.abspath(os.path.dirname(config.get('config_file_path')))) # Save last commit timestamp for entire site self.last_site_revision_timestamp = self.util.get_git_commit_timestamp( diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index ca46316..3e06800 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -27,13 +27,14 @@ class Util: This helps find git and calculate relevant dates. """ - def __init__(self, config={}): + def __init__(self, config: Dict, mkdocs_dir: str): """Initialize utility class.""" self.config = config self.repo_cache = {} ignore_commits_file = self.config.get("ignored_commits_file") - self.ignored_commits = self.parse_git_ignore_revs(ignore_commits_file) if ignore_commits_file else [] + ignore_commits_filepath = os.path.join(mkdocs_dir, ignore_commits_file) if ignore_commits_file else None + self.ignored_commits = self.parse_git_ignore_revs(ignore_commits_filepath) if ignore_commits_file else [] def _get_repo(self, path: str) -> Git: if not os.path.isdir(path): @@ -66,6 +67,7 @@ def get_git_commit_timestamp( int: commit date in unix timestamp, starts with the most recent commit. """ commit_timestamp = "" + n_ignored_commits = 0 # Determine the logging level # Only log warnings when plugin is set to strict. @@ -111,7 +113,7 @@ def get_git_commit_timestamp( ).split("\n") # process the commits for the file in reverse-chronological order. Ignore any commit that is on the - # ignored list. If the line is empty, we've reached the end and need to use the fallback behavior + # ignored list. If the line is empty, we've reached the end and need to use the fallback behavior. for line in lines: if not line: commit_timestamp = "" @@ -119,6 +121,9 @@ def get_git_commit_timestamp( commit, commit_timestamp = line.split(" ") if not any(commit.startswith(x) for x in self.ignored_commits): break + else: + n_ignored_commits += 1 + except (InvalidGitRepositoryError, NoSuchPathError) as err: if self.config.get('fallback_to_build_date'): @@ -165,10 +170,13 @@ def get_git_commit_timestamp( # create timestamp if commit_timestamp == "": commit_timestamp = time.time() - log( + msg = ( "[git-revision-date-localized-plugin] '%s' has no git logs, using current timestamp" % path ) + if n_ignored_commits: + msg += f" (ignored {n_ignored_commits} commits)" + log(msg) return int(commit_timestamp) @@ -222,10 +230,17 @@ def parse_git_ignore_revs(filename: str) -> List[str]: Whitespace, blanklines and comments starting with # are all ignored. """ result = [] - with open(filename, "rt") as f: - for line in f: - line = line.split("#", 1)[0].strip() - if not line: - continue - result.append(line) + + try: + with open(filename, "rt", encoding='utf-8') as f: + for line in f: + line = line.split("#", 1)[0].strip() + if not line: + continue + result.append(line) + except FileNotFoundError: + logger.error(f"File not found: {filename}") + except Exception as e: + logger.error(f"An error occurred while reading the file {filename}: {e}") + return result diff --git a/tests/fixtures/basic_project/.gitignore b/tests/fixtures/basic_project/.gitignore new file mode 100644 index 0000000..ccbfadb --- /dev/null +++ b/tests/fixtures/basic_project/.gitignore @@ -0,0 +1 @@ +site/ \ No newline at end of file diff --git a/tests/test_builds.py b/tests/test_builds.py index 9d5a6ca..acbc249 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -254,7 +254,7 @@ def validate_build(testproject_path, plugin_config: dict = {}): contents = page_with_tag.read_text(encoding="utf8") assert re.search(r"renders as\:\s[|\w].+", contents) - repo = Util(config=plugin_config) + repo = Util(config=plugin_config, mkdocs_dir=testproject_path) date_formats = repo.get_date_formats_for_timestamp( commit_timestamp=repo.get_git_commit_timestamp( path=str(testproject_path / "docs/page_with_tag.md"), @@ -380,7 +380,7 @@ def test_tags_are_replaced(tmp_path, mkdocs_file): # Make sure count_commits() works # We created 11 commits in setup_commit_history() with working_directory(testproject_path): - u = Util() + u = Util(config={}, mkdocs_dir=os.getcwd()) assert commit_count(u._get_repo("docs/page_with_tag.md")) == 11 @@ -682,7 +682,7 @@ def test_ignored_commits(tmp_path): # First test that the middle commit doesn't show up by default # January 23, 2022 is the date of the most recent commit - with open(str(testproject_path / "ignored-commits.txt"), "wt") as fp: + with open(str(testproject_path / "ignored-commits.txt"), "wt", encoding="utf-8") as fp: fp.write("") result = build_docs_setup(testproject_path) @@ -694,10 +694,10 @@ def test_ignored_commits(tmp_path): # Now mark the most recent change to page_with_tag as ignored # May 4, 2018 is the date of the second most recent commit - hash = repo.git.log("docs/page_with_tag.md", format="%H", n=1) + commit_hash = repo.git.log("docs/page_with_tag.md", format="%H", n=1) - with open(str(testproject_path / "ignored-commits.txt"), "wt") as fp: - fp.write(hash) + with open(str(testproject_path / "ignored-commits.txt"), "wt", encoding="utf-8") as fp: + fp.write(commit_hash) # should not raise warning result = build_docs_setup(testproject_path) From 60b7fac00da7c31449118d6b8e6e4c66a004ff83 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 12:13:17 +0000 Subject: [PATCH 07/44] fix unit test --- tests/test_builds.py | 2 +- tests/test_parse_git_ignore_revs.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_builds.py b/tests/test_builds.py index acbc249..e9b9fc8 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -705,4 +705,4 @@ def test_ignored_commits(tmp_path): page_with_tag = testproject_path / "site/page_with_tag/index.html" contents = page_with_tag.read_text(encoding="utf8") - assert "May 4, 2018" in contents \ No newline at end of file + assert "May 4, 2018" in contents diff --git a/tests/test_parse_git_ignore_revs.py b/tests/test_parse_git_ignore_revs.py index 93cf7fa..0d5ea56 100644 --- a/tests/test_parse_git_ignore_revs.py +++ b/tests/test_parse_git_ignore_revs.py @@ -3,14 +3,14 @@ import tempfile TEST_PARAMS = [ - (b"abc123\n", ["abc123"]), - (b"abc123 # comments are ignored\n", ["abc123"]), - (b"\n\n\n\n\nabc123\n\n\n\n\n", ["abc123"]), + ("abc123\n", ["abc123"]), + ("abc123 # comments are ignored\n", ["abc123"]), + ("\n\n\n\n\nabc123\n\n\n\n\n", ["abc123"]), ] @pytest.mark.parametrize("test_input,expected", TEST_PARAMS) def test_parse_git_ignore_revs(test_input, expected): - with tempfile.NamedTemporaryFile() as fp: + with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8') as fp: fp.write(test_input) fp.flush() assert Util.parse_git_ignore_revs(fp.name) == expected From b6bd1bf680b919b01875ec85d41c0688c749604a Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 12:19:02 +0000 Subject: [PATCH 08/44] fix windows unit tests --- tests/test_parse_git_ignore_revs.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_parse_git_ignore_revs.py b/tests/test_parse_git_ignore_revs.py index 0d5ea56..f22d9fd 100644 --- a/tests/test_parse_git_ignore_revs.py +++ b/tests/test_parse_git_ignore_revs.py @@ -1,6 +1,7 @@ from mkdocs_git_revision_date_localized_plugin.util import Util import pytest import tempfile +import os TEST_PARAMS = [ ("abc123\n", ["abc123"]), @@ -10,7 +11,10 @@ @pytest.mark.parametrize("test_input,expected", TEST_PARAMS) def test_parse_git_ignore_revs(test_input, expected): - with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8') as fp: + with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', delete=False) as fp: fp.write(test_input) - fp.flush() - assert Util.parse_git_ignore_revs(fp.name) == expected + temp_file_name = fp.name + try: + assert Util.parse_git_ignore_revs(temp_file_name) == expected + finally: + os.remove(temp_file_name) From 46f4daf585b8ed8eb8b2cf48c84b9497b077c5b1 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 12:24:36 +0000 Subject: [PATCH 09/44] bump version --- src/mkdocs_git_revision_date_localized_plugin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/__init__.py b/src/mkdocs_git_revision_date_localized_plugin/__init__.py index 9e2406e..d60e0c1 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/__init__.py +++ b/src/mkdocs_git_revision_date_localized_plugin/__init__.py @@ -1 +1 @@ -__version__ = "1.3.0" \ No newline at end of file +__version__ = "1.4.0" \ No newline at end of file From d1527d76410767724606e03ada6adcc0c04676fc Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 12:27:16 +0000 Subject: [PATCH 10/44] Catch errors explicitly, closes #98 --- .../util.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index 3e06800..c729c54 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -166,6 +166,21 @@ def get_git_commit_timestamp( " To ignore this error, set option 'fallback_to_build_date: true'" ) raise err + except Exception as err: + if self.config.get('fallback_to_build_date'): + log( + "[git-revision-date-localized-plugin] An unexpected error occurred: %s" + " Option 'fallback_to_build_date' set to 'true': Falling back to build date" + % str(err) + ) + commit_timestamp = time.time() + else: + logger.error( + "[git-revision-date-localized-plugin] An unexpected error occurred: %s" + " To ignore this error, set option 'fallback_to_build_date: true'" + % str(err) + ) + raise err # create timestamp if commit_timestamp == "": From 7b6edf1cfda55305af3bfed23e426cfcd065199e Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 12:33:50 +0000 Subject: [PATCH 11/44] Add tips to speed up builds, closes #119 --- .github/workflows/pythonpublish.yml | 3 +++ README.md | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 80beec3..ea26e3f 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -17,6 +17,9 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: '0' + sparse-checkout: | + docs + includes - name: Set up Python uses: actions/setup-python@v5 with: diff --git a/README.md b/README.md index e0a169a..20aa668 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,18 @@ This plugin needs access to the last commit that touched a specific file to be a - Bitbucket pipelines: set `clone: depth: full` (docs) - Azure Devops pipelines: set `Agent.Source.Git.ShallowFetchDepth` to something very high like `10e99` ([docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/pipeline-options-for-git?view=azure-devops#shallow-fetch)) +*Tip*: You can speed up your builds for large codebases by running git garbage collection (`git gc`) occasionly. You can also use _sparse checkouts_ to only apply the fetch-depth 0 for the folders we're interested (credits Martin [in this tweet](https://x.com/squidfunk/status/1705279829770150291)): + +```yaml +# example sparse checkout for github actions +- uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + docs + includes +``` + ## Documentation See [timvink.github.io/mkdocs-git-revision-date-localized-plugin](https://timvink.github.io/mkdocs-git-revision-date-localized-plugin/index.html). From c2d1dab47ba95be6d5b136f73e3dccd9c1b6c145 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 12:58:32 +0000 Subject: [PATCH 12/44] raise error when dubious git ownership is at fault --- src/mkdocs_git_revision_date_localized_plugin/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index c729c54..3e2fb14 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -154,6 +154,8 @@ def get_git_commit_timestamp( ) raise err except GitCommandNotFound as err: + if "detected dubious ownership" in str(err): + raise err if self.config.get('fallback_to_build_date'): log( "[git-revision-date-localized-plugin] Unable to perform command: 'git log'. Is git installed?" From f39ca495eb1d7f7023ec6e2db386afcb453f19bd Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 13:42:37 +0000 Subject: [PATCH 13/44] Add git hash and git tag information --- docs/available-variables.md | 16 ++++++- .../plugin.py | 16 ++++--- .../util.py | 44 ++++++++++++++++--- tests/test_builds.py | 14 +++--- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/docs/available-variables.md b/docs/available-variables.md index b23c42b..04eeb0e 100644 --- a/docs/available-variables.md +++ b/docs/available-variables.md @@ -1,8 +1,8 @@ # Available variables -This plugin offers the following variables: +This plugin offers the following timestamp variables: -| timestamp | description | +| variable | description | |:-----------|:------------| | `git_revision_date_localized` | Last git commit that touched a file. Enabled by default. | | `git_creation_date_localized` | First git commit that touched a file. Enable in [options](options.md). | @@ -33,6 +33,9 @@ To allow for more flexibility when overriding a theme there are also variables f - `page.meta.git_revision_date_localized_raw_iso_datetime` - `page.meta.git_revision_date_localized_raw_timeago` - `page.meta.git_revision_date_localized_raw_custom` + +Their are also available on the entire site level: + - `page.meta.git_site_revision_date_localized_raw_datetime` - `page.meta.git_site_revision_date_localized_raw_iso_date` - `page.meta.git_site_revision_date_localized_raw_date` @@ -49,6 +52,15 @@ And if you've enabled creation date in the config: - `page.meta.git_creation_date_localized_raw_timeago` - `page.meta.git_creation_date_localized_raw_custom` +We also expose the git hash and the git tag (empty if not tag associated with the commit): + +- `page.meta.git_revision_date_localized_hash` +- `page.meta.git_revision_date_localized_tag` +- `page.meta.git_site_revision_date_localized_hash` +- `page.meta.git_site_revision_date_localized_tag` +- `page.meta.git_creation_date_localized_hash` +- `page.meta.git_creation_date_localized_tag` + !!! warning "timeago.js dependency" The `*_timeago` variables require the [timeago.js](https://timeago.org/) dependency. This is automatically injected when the [option](options.md) `type: timeago` is set. Alternatively, you can add [timeago.js](https://timeago.org/) using the [`extra_javascript`](https://www.mkdocs.org/user-guide/configuration/#extra_javascript) option of MkDocs: diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index eea5727..f0bc549 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -70,7 +70,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: self.util = Util(config=self.config, mkdocs_dir=os.path.abspath(os.path.dirname(config.get('config_file_path')))) # Save last commit timestamp for entire site - self.last_site_revision_timestamp = self.util.get_git_commit_timestamp( + self.last_site_revision_hash, self.last_site_revision_timestamp = self.util.get_git_commit_timestamp( config.get('docs_dir') ) @@ -201,9 +201,9 @@ def on_page_markdown( # Retrieve git commit timestamp # Except for generated pages (f.e. by mkdocs-gen-files plugin) if getattr(page.file, "generated_by", None): - last_revision_timestamp = int(time.time()) + last_revision_hash, last_revision_timestamp = "", int(time.time()) else: - last_revision_timestamp = self.util.get_git_commit_timestamp( + last_revision_hash, last_revision_timestamp = self.util.get_git_commit_timestamp( path=page.file.abs_src_path, is_first_commit=False, ) @@ -221,6 +221,8 @@ def on_page_markdown( # Add to page meta information, for developers # Include variants without the CSS elements (raw date strings) page.meta["git_revision_date_localized"] = revision_date + page.meta["git_revision_date_localized_hash"] = last_revision_hash + page.meta["git_revision_date_localized_tag"] = self.util.get_tag_name_for_commit(last_revision_hash) revision_dates_raw = self.util.get_date_formats_for_timestamp(last_revision_timestamp, locale=locale, add_spans=False) for date_type, date_string in revision_dates_raw.items(): page.meta["git_revision_date_localized_raw_%s" % date_type] = date_string @@ -234,6 +236,8 @@ def on_page_markdown( ) # Also add site last updated information, for developers + page.meta["git_site_revision_date_localized_hash"] = self.last_site_revision_hash + page.meta["git_site_revision_date_localized_tag"] = self.util.get_tag_name_for_commit(self.last_site_revision_hash) site_dates = self.util.get_date_formats_for_timestamp(self.last_site_revision_timestamp, locale=locale, add_spans=True) site_date = site_dates[self.config["type"]] if self.config["type"] == "timeago": @@ -260,9 +264,9 @@ def on_page_markdown( # Retrieve git commit timestamp # Except for generated pages (f.e. by mkdocs-gen-files plugin) if getattr(page.file, "generated_by", None): - first_revision_timestamp = int(time.time()) + first_revision_hash, first_revision_timestamp = "", int(time.time()) else: - first_revision_timestamp = self.util.get_git_commit_timestamp( + first_revision_hash, first_revision_timestamp = self.util.get_git_commit_timestamp( path=page.file.abs_src_path, is_first_commit=True, ) @@ -279,6 +283,8 @@ def on_page_markdown( # Add to page meta information, for developers # Include variants without the CSS elements (raw date strings) + page.meta["git_creation_date_localized_hash"] = first_revision_hash + page.meta["git_creation_date_localized_tag"] = self.util.get_tag_name_for_commit(first_revision_hash) page.meta["git_creation_date_localized"] = creation_date creation_dates_raw = self.util.get_date_formats_for_timestamp(first_revision_timestamp, locale=locale, add_spans=False) for date_type, date_string in creation_dates_raw.items(): diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index 3e2fb14..388a448 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -16,7 +16,7 @@ NoSuchPathError, ) -from typing import Any, Dict, List +from typing import Dict, List, Tuple logger = logging.getLogger("mkdocs.plugins") @@ -53,7 +53,7 @@ def get_git_commit_timestamp( self, path: str, is_first_commit: bool = False - ) -> int: + ) -> Tuple[str, int]: """ Get a list of commit dates in unix timestamp, starts with the most recent commit. @@ -64,9 +64,10 @@ def get_git_commit_timestamp( is_first_commit (bool): retrieve commit timestamp when file was created. Returns: - int: commit date in unix timestamp, starts with the most recent commit. + tuple[str, int]: commit hash and commit date in unix timestamp. """ commit_timestamp = "" + commit_hash = "" n_ignored_commits = 0 # Determine the logging level @@ -118,8 +119,9 @@ def get_git_commit_timestamp( if not line: commit_timestamp = "" break - commit, commit_timestamp = line.split(" ") - if not any(commit.startswith(x) for x in self.ignored_commits): + commit_hash, commit_timestamp = line.split(" ") + breakpoint() + if not any(commit_hash.startswith(x) for x in self.ignored_commits): break else: n_ignored_commits += 1 @@ -195,7 +197,7 @@ def get_git_commit_timestamp( msg += f" (ignored {n_ignored_commits} commits)" log(msg) - return int(commit_timestamp) + return commit_hash, int(commit_timestamp) def get_date_formats_for_timestamp( self, @@ -261,3 +263,33 @@ def parse_git_ignore_revs(filename: str) -> List[str]: logger.error(f"An error occurred while reading the file {filename}: {e}") return result + + def get_tag_name_for_commit(self, commit_hash: str) -> str: + """ + Get the tag name for a specific commit. + + Args: + commit_hash (str): The commit hash to find tags for + + Returns: + str: Tag name if found, otherwise empty string + """ + if not commit_hash: + return "" + + try: + for path, git in self.repo_cache.items(): + try: + # Check if there's a tag pointing to this commit + tags = git.tag('--points-at', commit_hash, format='%(refname:short)') + if tags: + # Return first tag if multiple tags exist for the commit + return tags.split('\n')[0] + except GitCommandError: + # Continue checking other repositories in cache + continue + + return "" # No tag found for this commit + except Exception as e: + logger.debug(f"Error getting tag for commit {commit_hash}: {str(e)}") + return "" diff --git a/tests/test_builds.py b/tests/test_builds.py index e9b9fc8..9ff0438 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -255,11 +255,12 @@ def validate_build(testproject_path, plugin_config: dict = {}): assert re.search(r"renders as\:\s[|\w].+", contents) repo = Util(config=plugin_config, mkdocs_dir=testproject_path) - date_formats = repo.get_date_formats_for_timestamp( - commit_timestamp=repo.get_git_commit_timestamp( + commit_hash, commit_timestamp =repo.get_git_commit_timestamp( path=str(testproject_path / "docs/page_with_tag.md"), is_first_commit=False, - ), + ) + date_formats = repo.get_date_formats_for_timestamp( + commit_timestamp, locale=plugin_config['locale'], add_spans=True, ) @@ -268,10 +269,7 @@ def validate_build(testproject_path, plugin_config: dict = {}): assert any(searches), "No correct revision date formats output was found" if plugin_config.get("enable_creation_date"): - commit_timestamp=repo.get_git_commit_timestamp( - path=str(testproject_path / "docs/page_with_tag.md"), - is_first_commit=True, - ) + commit_hash, commit_timestamp = repo.get_git_commit_timestamp(path=str(testproject_path / "docs/page_with_tag.md"),is_first_commit=True,) assert commit_timestamp == 1500854705 date_formats = repo.get_date_formats_for_timestamp( commit_timestamp=commit_timestamp, @@ -283,7 +281,7 @@ def validate_build(testproject_path, plugin_config: dict = {}): assert any(searches), "No correct creation date formats output was found" if os.path.exists(str(testproject_path / "docs/subfolder/page_with_renamed.md")): - commit_timestamp=repo.get_git_commit_timestamp( + commit_hash, commit_timestamp=repo.get_git_commit_timestamp( path=str(testproject_path / "docs/subfolder/page_with_renamed.md"), is_first_commit=True ) From 8fd58fa57d53a49d10945a59d1710894ce0fcb7a Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 13:51:03 +0000 Subject: [PATCH 14/44] Add stdout to error when build fails --- tests/test_builds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_builds.py b/tests/test_builds.py index 9ff0438..f873aa3 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -301,7 +301,7 @@ def validate_mkdocs_file(temp_path: str, mkdocs_yml_file: str): ) setup_commit_history(testproject_path) result = build_docs_setup(testproject_path) - assert result.exit_code == 0, "'mkdocs build' command failed" + assert result.exit_code == 0, f"'mkdocs build' command failed with output:\n{result.stdout}" # validate build with locale retrieved from mkdocs config file validate_build( From f07898173fb0d1363ca89e439b966100331a0748 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 14:16:25 +0000 Subject: [PATCH 15/44] remove breakpoint() --- mkdocs.yml | 4 ++-- src/mkdocs_git_revision_date_localized_plugin/util.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index ab61ca8..5859daa 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,8 +60,8 @@ markdown_extensions: - pymdownx.tabbed: alternate_style: true - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg options: custom_icons: - site/overrides/.icons \ No newline at end of file diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index 388a448..10b1bd3 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -120,7 +120,6 @@ def get_git_commit_timestamp( commit_timestamp = "" break commit_hash, commit_timestamp = line.split(" ") - breakpoint() if not any(commit_hash.startswith(x) for x in self.ignored_commits): break else: From 420de89295c15d97c23e890ee9c810ded93b7dfc Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 14:24:21 +0000 Subject: [PATCH 16/44] Update README --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 20aa668..966a547 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,12 @@ # mkdocs-git-revision-date-localized-plugin -[MkDocs](https://www.mkdocs.org/) plugin that enables displaying the date of the last git modification of a page. The plugin uses [babel](https://github.com/python-babel/babel/tree/master/babel) and [timeago.js](https://github.com/hustcc/timeago.js) to provide different localized date formats. Initial fork from [mkdocs-git-revision-date-plugin](https://github.com/zhaoterryy/mkdocs-git-revision-date-plugin). +[MkDocs](https://www.mkdocs.org/) plugin that enables displaying the date of the last git modification of a page. The plugin uses [babel](https://github.com/python-babel/babel/tree/master/babel) and [timeago.js](https://github.com/hustcc/timeago.js) to provide different localized date formats. Works with [`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/), see [adding a git repository](https://squidfunk.github.io/mkdocs-material/setup/adding-a-git-repository/). ![demo](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/raw/master/demo_screencast.gif) (*Example when used together with the [mkdocs-material](https://github.com/squidfunk/mkdocs-material) theme*) -Other related MkDocs plugins: - -- [mkdocs-git-authors-plugin](https://github.com/timvink/mkdocs-git-authors-plugin) for displaying the authors from git -- [mkdocs-git-committers-plugin](https://github.com/byrnereese/mkdocs-git-committers-plugin) for displaying authors' github user profiles -- [mkdocs-document-dates](https://github.com/jaywhj/mkdocs-document-dates) for displaying dates based on file creation and modification dates. - ## Setup Install the plugin using `pip3` with the following command: @@ -69,3 +63,10 @@ See [timvink.github.io/mkdocs-git-revision-date-localized-plugin](https://timvin ## Contributing Contributions are very welcome! Please read [CONTRIBUTING.md](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/blob/master/CONTRIBUTING.md) before putting in any work. + +## Related MkDocs plugins: + +- [mkdocs-git-authors-plugin](https://github.com/timvink/mkdocs-git-authors-plugin) for displaying the authors from git +- [mkdocs-git-committers-plugin-2](https://github.com/ojacques/mkdocs-git-committers-plugin-2) for displaying authors' github user profiles +- [mkdocs-document-dates](https://github.com/jaywhj/mkdocs-document-dates) for displaying dates based on file creation and modification dates. +- Initial fork from [mkdocs-git-revision-date-plugin](https://github.com/zhaoterryy/mkdocs-git-revision-date-plugin). \ No newline at end of file From 596273fd8db1d7c51a851a9ec204db3a13a1ead6 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 14:33:04 +0000 Subject: [PATCH 17/44] Ensure creation date is never later than revision date --- src/mkdocs_git_revision_date_localized_plugin/plugin.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index f0bc549..664639b 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -271,6 +271,13 @@ def on_page_markdown( is_first_commit=True, ) + if first_revision_timestamp < last_revision_timestamp: + # See also https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/111 + msg = "First revision timestamp is older than last revision timestamp for page %s. " % page.file.src_path + msg += "This can be due to a quick in `git` follow behaviour. You can try to set `enable_git_follow: false` in the plugin configuration. + logging.warning(msg) + first_revision_hash, first_revision_timestamp = last_revision_hash, last_revision_timestamp + # Creation date formats creation_dates = self.util.get_date_formats_for_timestamp(first_revision_timestamp, locale=locale, add_spans=True) creation_date = creation_dates[self.config["type"]] From 9319b59add21aa45f4d3cdb40b1fb6acb6a127a4 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 14:36:50 +0000 Subject: [PATCH 18/44] fix typos --- src/mkdocs_git_revision_date_localized_plugin/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 664639b..f3c180a 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -271,10 +271,10 @@ def on_page_markdown( is_first_commit=True, ) - if first_revision_timestamp < last_revision_timestamp: + if first_revision_timestamp > last_revision_timestamp: # See also https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/111 msg = "First revision timestamp is older than last revision timestamp for page %s. " % page.file.src_path - msg += "This can be due to a quick in `git` follow behaviour. You can try to set `enable_git_follow: false` in the plugin configuration. + msg += "This can be due to a quick in `git` follow behaviour. You can try to set `enable_git_follow: false` in the plugin configuration." logging.warning(msg) first_revision_hash, first_revision_timestamp = last_revision_hash, last_revision_timestamp From 7ab68e9c4978a061552b7d0ea677d4776d22bf44 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 14:42:10 +0000 Subject: [PATCH 19/44] ruff format codebase --- .../__init__.py | 2 +- .../ci.py | 4 +- .../dates.py | 13 +-- .../exclude.py | 1 + .../plugin.py | 106 ++++++++++-------- .../util.py | 85 +++++++------- 6 files changed, 105 insertions(+), 106 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/__init__.py b/src/mkdocs_git_revision_date_localized_plugin/__init__.py index d60e0c1..3e8d9f9 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/__init__.py +++ b/src/mkdocs_git_revision_date_localized_plugin/__init__.py @@ -1 +1 @@ -__version__ = "1.4.0" \ No newline at end of file +__version__ = "1.4.0" diff --git a/src/mkdocs_git_revision_date_localized_plugin/ci.py b/src/mkdocs_git_revision_date_localized_plugin/ci.py index e203568..22b3bb4 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/ci.py +++ b/src/mkdocs_git_revision_date_localized_plugin/ci.py @@ -20,7 +20,6 @@ def raise_ci_warnings(repo) -> None: n_commits = commit_count(repo) - # Gitlab Runners if os.getenv("GITLAB_CI") is not None and n_commits < 50: # Default is GIT_DEPTH of 50 for gitlab @@ -76,7 +75,6 @@ def raise_ci_warnings(repo) -> None: ) - def commit_count(repo) -> int: """ Determine the number of commits in a repository. @@ -87,7 +85,7 @@ def commit_count(repo) -> int: Returns: count (int): Number of commits. """ - return int(repo.rev_list('--count','HEAD')) + return int(repo.rev_list("--count", "HEAD")) def is_shallow_clone(repo) -> bool: diff --git a/src/mkdocs_git_revision_date_localized_plugin/dates.py b/src/mkdocs_git_revision_date_localized_plugin/dates.py index 17266fd..4eafe7e 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/dates.py +++ b/src/mkdocs_git_revision_date_localized_plugin/dates.py @@ -1,4 +1,3 @@ - from babel.dates import format_date, get_timezone from datetime import datetime, timezone @@ -6,10 +5,7 @@ def get_date_formats( - unix_timestamp: float, - locale: str = "en", - time_zone: str = "UTC", - custom_format: str = "%d. %B %Y" + unix_timestamp: float, locale: str = "en", time_zone: str = "UTC", custom_format: str = "%d. %B %Y" ) -> Dict[str, Any]: """ Calculate different date formats / types. @@ -25,11 +21,9 @@ def get_date_formats( """ assert time_zone is not None assert locale is not None - + utc_revision_date = datetime.fromtimestamp(int(unix_timestamp), tz=timezone.utc) - loc_revision_date = utc_revision_date.replace( - tzinfo=get_timezone("UTC") - ).astimezone(get_timezone(time_zone)) + loc_revision_date = utc_revision_date.replace(tzinfo=get_timezone("UTC")).astimezone(get_timezone(time_zone)) return { "date": format_date(loc_revision_date, format="long", locale=locale), @@ -44,4 +38,3 @@ def get_date_formats( "timeago": '' % (loc_revision_date.isoformat(), locale), "custom": loc_revision_date.strftime(custom_format), } - \ No newline at end of file diff --git a/src/mkdocs_git_revision_date_localized_plugin/exclude.py b/src/mkdocs_git_revision_date_localized_plugin/exclude.py index 0a44688..a5d91d0 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/exclude.py +++ b/src/mkdocs_git_revision_date_localized_plugin/exclude.py @@ -3,6 +3,7 @@ Inspired by https://github.com/apenwarr/mkdocs-exclude """ + import os import fnmatch from typing import List diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index f3c180a..a33f96b 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -4,6 +4,7 @@ https://www.mkdocs.org/ https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/ """ + import logging import re import os @@ -26,6 +27,7 @@ HERE = os.path.dirname(os.path.abspath(__file__)) + class GitRevisionDateLocalizedPlugin(BasePlugin): """ Mkdocs plugin to add revision date from Git. @@ -62,16 +64,18 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: Returns: dict: global configuration object """ - if not self.config.get('enabled'): + if not self.config.get("enabled"): return config - - assert self.config['type'] in ["date","datetime","iso_date","iso_datetime","timeago","custom"] - self.util = Util(config=self.config, mkdocs_dir=os.path.abspath(os.path.dirname(config.get('config_file_path')))) + assert self.config["type"] in ["date", "datetime", "iso_date", "iso_datetime", "timeago", "custom"] + + self.util = Util( + config=self.config, mkdocs_dir=os.path.abspath(os.path.dirname(config.get("config_file_path"))) + ) # Save last commit timestamp for entire site self.last_site_revision_hash, self.last_site_revision_timestamp = self.util.get_git_commit_timestamp( - config.get('docs_dir') + config.get("docs_dir") ) # Get locale from plugin configuration @@ -80,24 +84,22 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: # theme locale if "theme" in config and "language" in config.get("theme"): custom_theme = config.get("theme") - theme_locale = custom_theme["language"] if Version(mkdocs_version) >= Version("1.6.0") else custom_theme._vars.get("language") - logging.debug( - "Locale '%s' extracted from the custom theme: '%s'" - % (theme_locale, custom_theme.name) + theme_locale = ( + custom_theme["language"] + if Version(mkdocs_version) >= Version("1.6.0") + else custom_theme._vars.get("language") ) + logging.debug("Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name)) elif "theme" in config and "locale" in config.get("theme"): custom_theme = config.get("theme") - theme_locale = custom_theme.locale if Version(mkdocs_version) >= Version("1.6.0") else custom_theme._vars.get("locale") - logging.debug( - "Locale '%s' extracted from the custom theme: '%s'" - % (theme_locale, custom_theme.name) + theme_locale = ( + custom_theme.locale if Version(mkdocs_version) >= Version("1.6.0") else custom_theme._vars.get("locale") ) + logging.debug("Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name)) else: theme_locale = None - logging.debug( - "No locale found in theme configuration (or no custom theme set)" - ) + logging.debug("No locale found in theme configuration (or no custom theme set)") # First prio: plugin locale if plugin_locale: @@ -106,10 +108,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: # Second prio: theme locale elif theme_locale: locale_set = theme_locale - logging.debug( - "Locale not set in plugin. Fallback to theme configuration: %s" - % locale_set - ) + logging.debug("Locale not set in plugin. Fallback to theme configuration: %s" % locale_set) # Lastly, fallback is English else: locale_set = "en" @@ -117,19 +116,17 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: # Validate locale locale_set = str(locale_set) - assert len(locale_set) == 2, "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" + assert len(locale_set) == 2, ( + "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" + ) # set locale also in plugin configuration self.config["locale"] = locale_set # Add pointers to support files for timeago.js if self.config.get("type") == "timeago": - config["extra_javascript"] = ["js/timeago_mkdocs_material.js"] + config[ - "extra_javascript" - ] - config["extra_javascript"] = ["js/timeago.min.js"] + config[ - "extra_javascript" - ] + config["extra_javascript"] = ["js/timeago_mkdocs_material.js"] + config["extra_javascript"] + config["extra_javascript"] = ["js/timeago.min.js"] + config["extra_javascript"] config["extra_css"] = ["css/timeago.css"] + config["extra_css"] # Compatibility with mkdocs-static-i18n @@ -139,12 +136,10 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: msg = "[git-revision-date-localized] should be defined after the i18n plugin in your mkdocs.yml file. " msg += "This is because i18n adds a 'locale' variable to markdown pages that this plugin supports." raise ConfigurationError(msg) - + return config - def on_page_markdown( - self, markdown: str, page: Page, config: config_options.Config, files, **kwargs - ) -> str: + def on_page_markdown(self, markdown: str, page: Page, config: config_options.Config, files, **kwargs) -> str: """ Replace jinja2 tags in markdown and templates with the localized dates. @@ -164,7 +159,7 @@ def on_page_markdown( Returns: str: Markdown source text of page as string """ - if not self.config.get('enabled'): + if not self.config.get("enabled"): return markdown # Exclude pages specified in config @@ -185,31 +180,35 @@ def on_page_markdown( # Second prio is a frontmatter variable 'locale' set in the markdown if not locale: if "locale" in page.meta: - locale = page.meta['locale'] + locale = page.meta["locale"] # Finally, if no page locale set, we take the locale determined on_config() if not locale: locale = self.config.get("locale") - + # MkDocs supports 2-letter and 5-letter locales # https://www.mkdocs.org/user-guide/localizing-your-theme/#supported-locales # We need the 2 letter variant if len(locale) == 5: locale = locale[:2] - assert len(locale) == 2, "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" - + assert len(locale) == 2, ( + "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" + ) + # Retrieve git commit timestamp # Except for generated pages (f.e. by mkdocs-gen-files plugin) if getattr(page.file, "generated_by", None): last_revision_hash, last_revision_timestamp = "", int(time.time()) else: last_revision_hash, last_revision_timestamp = self.util.get_git_commit_timestamp( - path=page.file.abs_src_path, - is_first_commit=False, + path=page.file.abs_src_path, + is_first_commit=False, ) # Last revision date - revision_dates = self.util.get_date_formats_for_timestamp(last_revision_timestamp, locale=locale, add_spans=True) + revision_dates = self.util.get_date_formats_for_timestamp( + last_revision_timestamp, locale=locale, add_spans=True + ) revision_date = revision_dates[self.config["type"]] # timeago output is dynamic, which breaks when you print a page @@ -223,7 +222,9 @@ def on_page_markdown( page.meta["git_revision_date_localized"] = revision_date page.meta["git_revision_date_localized_hash"] = last_revision_hash page.meta["git_revision_date_localized_tag"] = self.util.get_tag_name_for_commit(last_revision_hash) - revision_dates_raw = self.util.get_date_formats_for_timestamp(last_revision_timestamp, locale=locale, add_spans=False) + revision_dates_raw = self.util.get_date_formats_for_timestamp( + last_revision_timestamp, locale=locale, add_spans=False + ) for date_type, date_string in revision_dates_raw.items(): page.meta["git_revision_date_localized_raw_%s" % date_type] = date_string @@ -237,13 +238,19 @@ def on_page_markdown( # Also add site last updated information, for developers page.meta["git_site_revision_date_localized_hash"] = self.last_site_revision_hash - page.meta["git_site_revision_date_localized_tag"] = self.util.get_tag_name_for_commit(self.last_site_revision_hash) - site_dates = self.util.get_date_formats_for_timestamp(self.last_site_revision_timestamp, locale=locale, add_spans=True) + page.meta["git_site_revision_date_localized_tag"] = self.util.get_tag_name_for_commit( + self.last_site_revision_hash + ) + site_dates = self.util.get_date_formats_for_timestamp( + self.last_site_revision_timestamp, locale=locale, add_spans=True + ) site_date = site_dates[self.config["type"]] if self.config["type"] == "timeago": site_date += site_dates["iso_date"] page.meta["git_site_revision_date_localized"] = site_date - site_dates_raw = self.util.get_date_formats_for_timestamp(self.last_site_revision_timestamp, locale=locale, add_spans=False) + site_dates_raw = self.util.get_date_formats_for_timestamp( + self.last_site_revision_timestamp, locale=locale, add_spans=False + ) for date_type, date_string in site_dates_raw.items(): page.meta["git_site_revision_date_localized_raw_%s" % date_type] = date_string @@ -255,12 +262,11 @@ def on_page_markdown( flags=re.IGNORECASE, ) - # If creation date not enabled, return markdown # This is for speed: prevents another `git log` operation each file if not self.config.get("enable_creation_date"): return markdown - + # Retrieve git commit timestamp # Except for generated pages (f.e. by mkdocs-gen-files plugin) if getattr(page.file, "generated_by", None): @@ -279,7 +285,9 @@ def on_page_markdown( first_revision_hash, first_revision_timestamp = last_revision_hash, last_revision_timestamp # Creation date formats - creation_dates = self.util.get_date_formats_for_timestamp(first_revision_timestamp, locale=locale, add_spans=True) + creation_dates = self.util.get_date_formats_for_timestamp( + first_revision_timestamp, locale=locale, add_spans=True + ) creation_date = creation_dates[self.config["type"]] # timeago output is dynamic, which breaks when you print a page @@ -293,7 +301,9 @@ def on_page_markdown( page.meta["git_creation_date_localized_hash"] = first_revision_hash page.meta["git_creation_date_localized_tag"] = self.util.get_tag_name_for_commit(first_revision_hash) page.meta["git_creation_date_localized"] = creation_date - creation_dates_raw = self.util.get_date_formats_for_timestamp(first_revision_timestamp, locale=locale, add_spans=False) + creation_dates_raw = self.util.get_date_formats_for_timestamp( + first_revision_timestamp, locale=locale, add_spans=False + ) for date_type, date_string in creation_dates_raw.items(): page.meta["git_creation_date_localized_raw_%s" % date_type] = date_string @@ -314,7 +324,7 @@ def on_post_build(self, config: Dict[str, Any], **kwargs) -> None: Adds the timeago assets to the build. """ # Add timeago files: - if self.config.get("type") == "timeago" and self.config.get('enabled'): + if self.config.get("type") == "timeago" and self.config.get("enabled"): files = [ "js/timeago.min.js", "js/timeago_mkdocs_material.js", diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index 10b1bd3..43dbedb 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -1,4 +1,5 @@ """Utility class for mkdocs plugin.""" + import logging import os import time @@ -48,12 +49,7 @@ def _get_repo(self, path: str) -> Git: return self.repo_cache[path] - - def get_git_commit_timestamp( - self, - path: str, - is_first_commit: bool = False - ) -> Tuple[str, int]: + def get_git_commit_timestamp(self, path: str, is_first_commit: bool = False) -> Tuple[str, int]: """ Get a list of commit dates in unix timestamp, starts with the most recent commit. @@ -73,7 +69,7 @@ def get_git_commit_timestamp( # Determine the logging level # Only log warnings when plugin is set to strict. # That way, users turn those into errors using mkdocs build --strict - if self.config.get('strict'): + if self.config.get("strict"): log = logger.warning else: log = logger.info @@ -86,7 +82,7 @@ def get_git_commit_timestamp( realpath = os.path.realpath(path) git = self._get_repo(realpath) - follow_option=self.config.get('enable_git_follow') + follow_option = self.config.get("enable_git_follow") # Ignored commits are only considered for the most recent update, not for creation if is_first_commit: @@ -94,7 +90,7 @@ def get_git_commit_timestamp( commit_timestamp = git.log( realpath, date="unix", format="%at", diff_filter="Ar", no_show_signature=True, follow=follow_option ) - # A file can be created multiple times, through a file renamed. + # A file can be created multiple times, through a file renamed. # Commits are ordered with most recent commit first # Get the oldest commit only if commit_timestamp != "": @@ -102,17 +98,27 @@ def get_git_commit_timestamp( else: # Latest commit touching a specific file commit_timestamp = git.log( - realpath, date="unix", format="%at", - diff_filter="r", n=1, no_show_signature=True, follow=follow_option, - ignore_all_space=True, ignore_blank_lines=True + realpath, + date="unix", + format="%at", + diff_filter="r", + n=1, + no_show_signature=True, + follow=follow_option, + ignore_all_space=True, + ignore_blank_lines=True, ) # Retrieve the history for the file in the format # The maximum number of commits we will ever need to examine is 1 more than the number of ignored commits. lines = git.log( - realpath, date="unix", format="%H %at", n=len(self.ignored_commits)+1, no_show_signature=True, + realpath, + date="unix", + format="%H %at", + n=len(self.ignored_commits) + 1, + no_show_signature=True, ).split("\n") - + # process the commits for the file in reverse-chronological order. Ignore any commit that is on the # ignored list. If the line is empty, we've reached the end and need to use the fallback behavior. for line in lines: @@ -125,9 +131,8 @@ def get_git_commit_timestamp( else: n_ignored_commits += 1 - except (InvalidGitRepositoryError, NoSuchPathError) as err: - if self.config.get('fallback_to_build_date'): + if self.config.get("fallback_to_build_date"): log( "[git-revision-date-localized-plugin] Unable to find a git directory and/or git is not installed." " Option 'fallback_to_build_date' set to 'true': Falling back to build date" @@ -140,24 +145,22 @@ def get_git_commit_timestamp( ) raise err except GitCommandError as err: - if self.config.get('fallback_to_build_date'): + if self.config.get("fallback_to_build_date"): log( "[git-revision-date-localized-plugin] Unable to read git logs of '%s'. Is git log readable?" - " Option 'fallback_to_build_date' set to 'true': Falling back to build date" - % path + " Option 'fallback_to_build_date' set to 'true': Falling back to build date" % path ) commit_timestamp = time.time() else: logger.error( "[git-revision-date-localized-plugin] Unable to read git logs of '%s'. " - " To ignore this error, set option 'fallback_to_build_date: true'" - % path + " To ignore this error, set option 'fallback_to_build_date: true'" % path ) raise err except GitCommandNotFound as err: if "detected dubious ownership" in str(err): raise err - if self.config.get('fallback_to_build_date'): + if self.config.get("fallback_to_build_date"): log( "[git-revision-date-localized-plugin] Unable to perform command: 'git log'. Is git installed?" " Option 'fallback_to_build_date' set to 'true': Falling back to build date" @@ -170,28 +173,23 @@ def get_git_commit_timestamp( ) raise err except Exception as err: - if self.config.get('fallback_to_build_date'): + if self.config.get("fallback_to_build_date"): log( "[git-revision-date-localized-plugin] An unexpected error occurred: %s" - " Option 'fallback_to_build_date' set to 'true': Falling back to build date" - % str(err) + " Option 'fallback_to_build_date' set to 'true': Falling back to build date" % str(err) ) commit_timestamp = time.time() else: logger.error( "[git-revision-date-localized-plugin] An unexpected error occurred: %s" - " To ignore this error, set option 'fallback_to_build_date: true'" - % str(err) + " To ignore this error, set option 'fallback_to_build_date: true'" % str(err) ) raise err # create timestamp if commit_timestamp == "": commit_timestamp = time.time() - msg = ( - "[git-revision-date-localized-plugin] '%s' has no git logs, using current timestamp" - % path - ) + msg = "[git-revision-date-localized-plugin] '%s' has no git logs, using current timestamp" % path if n_ignored_commits: msg += f" (ignored {n_ignored_commits} commits)" log(msg) @@ -216,23 +214,22 @@ def get_date_formats_for_timestamp( dict: Localized date variants. """ date_formats = get_date_formats( - unix_timestamp=commit_timestamp, + unix_timestamp=commit_timestamp, time_zone=self.config.get("timezone"), locale=locale, - custom_format=self.config.get('custom_format') + custom_format=self.config.get("custom_format"), ) if add_spans: date_formats = self.add_spans(date_formats) return date_formats - @staticmethod def add_spans(date_formats: Dict[str, str]) -> Dict[str, str]: """ Wraps the date string in elements with CSS identifiers. """ - datetime_string = date_formats['datetime'] + datetime_string = date_formats["datetime"] for date_type, date_string in date_formats.items(): date_formats[date_type] = ( '%s' @@ -248,9 +245,9 @@ def parse_git_ignore_revs(filename: str) -> List[str]: Whitespace, blanklines and comments starting with # are all ignored. """ result = [] - + try: - with open(filename, "rt", encoding='utf-8') as f: + with open(filename, "rt", encoding="utf-8") as f: for line in f: line = line.split("#", 1)[0].strip() if not line: @@ -260,34 +257,34 @@ def parse_git_ignore_revs(filename: str) -> List[str]: logger.error(f"File not found: {filename}") except Exception as e: logger.error(f"An error occurred while reading the file {filename}: {e}") - + return result def get_tag_name_for_commit(self, commit_hash: str) -> str: """ Get the tag name for a specific commit. - + Args: commit_hash (str): The commit hash to find tags for - + Returns: str: Tag name if found, otherwise empty string """ if not commit_hash: return "" - + try: for path, git in self.repo_cache.items(): try: # Check if there's a tag pointing to this commit - tags = git.tag('--points-at', commit_hash, format='%(refname:short)') + tags = git.tag("--points-at", commit_hash, format="%(refname:short)") if tags: # Return first tag if multiple tags exist for the commit - return tags.split('\n')[0] + return tags.split("\n")[0] except GitCommandError: # Continue checking other repositories in cache continue - + return "" # No tag found for this commit except Exception as e: logger.debug(f"Error getting tag for commit {commit_hash}: {str(e)}") From 9eb8511854b48ce161199ec51aada51d379fbcf6 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 19:59:26 +0000 Subject: [PATCH 20/44] support 5 letter locale settings --- docs/howto/specify-locale.md | 3 +- docs/options.md | 22 +++++--- .../dates.py | 55 ++++++++++++++++++- .../plugin.py | 19 ++----- tests/test_dates.py | 33 ++++++++++- 5 files changed, 105 insertions(+), 27 deletions(-) diff --git a/docs/howto/specify-locale.md b/docs/howto/specify-locale.md index cfeb46d..1123898 100644 --- a/docs/howto/specify-locale.md +++ b/docs/howto/specify-locale.md @@ -1,6 +1,6 @@ # Specify a locale -`locale` is a two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code that `git-revision-date-localized` uses to display dates in your preferred language. +`locale` is aa two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code (f.e. `en`) or [5-letter language code with added territory/region/country](https://www.mkdocs.org/user-guide/localizing-your-theme/#supported-locales) (`en_US`) that `git-revision-date-localized` uses to display dates in your preferred language. For example: @@ -68,6 +68,7 @@ plugins: If no `locale` is specified anywhere, the fallback is English with the US date format (`en`). !!! info "Supported locales" + - When used in combination with `type: date` or `type: datetime`, translation is done using [babel](https://github.com/python-babel/babel) which supports [these locales](http://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html) - When used in combination with `type: timeago` then [timeago.js](https://github.com/hustcc/timeago.js) is added to your website, which supports [these locales](https://github.com/hustcc/timeago.js/tree/master/src/lang). If you specify a locale not supported by timeago.js, the fallback is English (`en`) diff --git a/docs/options.md b/docs/options.md index 1f00e52..c0835d6 100644 --- a/docs/options.md +++ b/docs/options.md @@ -53,17 +53,19 @@ Default is `UTC`. Specify a time zone database name ([reference](https://en.wiki ## `locale` -Default is `None`. Specify a two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code to display dates in your preferred language. Note this plugin supports many different ways to [specify the locale](howto/specify-locale.md), but if not specified anywhere the fallback is English (`en`). +Default is `None`. Specify a two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code (f.e. `en`) or [5-letter language code with added territory/region/country](https://www.mkdocs.org/user-guide/localizing-your-theme/#supported-locales) (`en_US`) to display dates in your preferred language. Note this plugin supports many different ways to [specify the locale](howto/specify-locale.md), but if not specified _anywhere_ the fallback will be English (`en`). `locale` is used to translate timestamps to dates when `type: date` or `type: datetime` (using [babel](https://github.com/python-babel/babel)) as well as to translate datetimes to a relative timeago string when `type: timeago` (using [timeago.js](https://github.com/hustcc/timeago.js)). Example outputs: ```yaml -April 27, 2021 # `locale: en` with `type: date` (default) -April 27, 2021 13:11:28 # `locale: en` with `type: datetime` -2 weeks ago # `locale: en` with `type: timeago` -27 de marzo de 2021 # `locale: es` with `type: date` -27 de marzo de 2021 13:57:28 # `locale: es` with `type: datetime` -hace 2 semanas # `locale: es` with `type: timeago` +# `locale: en` +April 27, 2021 # with `type: date` (default) +April 27, 2021 13:11:28 # with `type: datetime` +2 weeks ago # with `type: timeago` +# `locale: es` +27 de marzo de 2021 # with `type: date` +27 de marzo de 2021 13:57:28 # with `type: datetime` +hace 2 semanas # with `type: timeago` ``` === ":octicons-file-code-16: mkdocs.yml" @@ -71,9 +73,13 @@ hace 2 semanas # `locale: es` with `type: timeago` ```yaml plugins: - git-revision-date-localized: - locale: en + locale: en_US ``` +!!! note when using `type: timeago` + + When using `type: timeago` then [timeago.js](https://github.com/hustcc/timeago.js) is added to your website, which supports [these locales](https://github.com/hustcc/timeago.js/tree/master/src/lang). If you specify a locale not supported by timeago.js, the fallback is English (`en`). It might happen that your specific locale is supported by babel (used by date formats) but not by timeago. In that case open an issue with this plugin. + ## `fallback_to_build_date` diff --git a/src/mkdocs_git_revision_date_localized_plugin/dates.py b/src/mkdocs_git_revision_date_localized_plugin/dates.py index 4eafe7e..3fd4dd6 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/dates.py +++ b/src/mkdocs_git_revision_date_localized_plugin/dates.py @@ -1,4 +1,4 @@ -from babel.dates import format_date, get_timezone +from babel.dates import format_date, get_timezone, format_datetime from datetime import datetime, timezone from typing import Any, Dict @@ -36,5 +36,56 @@ def get_date_formats( "iso_date": loc_revision_date.strftime("%Y-%m-%d"), "iso_datetime": loc_revision_date.strftime("%Y-%m-%d %H:%M:%S"), "timeago": '' % (loc_revision_date.isoformat(), locale), - "custom": loc_revision_date.strftime(custom_format), + "custom": format_datetime(loc_revision_date, format=strftime_to_babel_format(custom_format), locale=locale), } + + +def strftime_to_babel_format(fmt: str) -> str: + """ + Convert strftime format string to Babel format pattern. + + Args: + fmt (str): strftime format string + + Returns: + str: Babel format pattern + """ + # Dictionary mapping strftime directives to Babel format patterns + mapping = { + '%a': 'EEE', # Weekday abbreviated + '%A': 'EEEE', # Weekday full + '%b': 'MMM', # Month abbreviated + '%B': 'MMMM', # Month full + '%c': '', # Locale's date and time (not directly mappable) + '%d': 'dd', # Day of month zero-padded + '%-d': 'd', # Day of month + '%e': 'd', # Day of month space-padded + '%f': 'SSSSSS', # Microsecond + '%H': 'HH', # Hour 24h zero-padded + '%-H': 'H', # Hour 24h + '%I': 'hh', # Hour 12h zero-padded + '%-I': 'h', # Hour 12h + '%j': 'DDD', # Day of year + '%m': 'MM', # Month zero-padded + '%-m': 'M', # Month + '%M': 'mm', # Minute zero-padded + '%-M': 'm', # Minute + '%p': 'a', # AM/PM + '%S': 'ss', # Second zero-padded + '%-S': 's', # Second + '%w': 'e', # Weekday as number + '%W': 'w', # Week of year + '%x': '', # Locale's date (not directly mappable) + '%X': '', # Locale's time (not directly mappable) + '%y': 'yy', # Year without century + '%Y': 'yyyy', # Year with century + '%z': 'Z', # UTC offset + '%Z': 'z', # Timezone name + '%%': '%' # Literal % + } + + result = fmt + for strftime_code, babel_code in mapping.items(): + result = result.replace(strftime_code, babel_code) + + return result \ No newline at end of file diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index a33f96b..49747da 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -81,7 +81,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: # Get locale from plugin configuration plugin_locale = self.config.get("locale", None) - # theme locale + # Get locale from theme configuration if "theme" in config and "language" in config.get("theme"): custom_theme = config.get("theme") theme_locale = ( @@ -96,7 +96,6 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: custom_theme.locale if Version(mkdocs_version) >= Version("1.6.0") else custom_theme._vars.get("locale") ) logging.debug("Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name)) - else: theme_locale = None logging.debug("No locale found in theme configuration (or no custom theme set)") @@ -116,9 +115,6 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: # Validate locale locale_set = str(locale_set) - assert len(locale_set) == 2, ( - "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" - ) # set locale also in plugin configuration self.config["locale"] = locale_set @@ -173,7 +169,6 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con # First prio is use mkdocs-static-i18n locale if set try: locale = page.file.locale - except AttributeError: locale = None @@ -183,18 +178,12 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con locale = page.meta["locale"] # Finally, if no page locale set, we take the locale determined on_config() + # (fourth prio is plugin configuration) + # (firth prio is theme configuration) + # (sixth prio is fallback to English) if not locale: locale = self.config.get("locale") - # MkDocs supports 2-letter and 5-letter locales - # https://www.mkdocs.org/user-guide/localizing-your-theme/#supported-locales - # We need the 2 letter variant - if len(locale) == 5: - locale = locale[:2] - assert len(locale) == 2, ( - "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" - ) - # Retrieve git commit timestamp # Except for generated pages (f.e. by mkdocs-gen-files plugin) if getattr(page.file, "generated_by", None): diff --git a/tests/test_dates.py b/tests/test_dates.py index bbe0e94..c98750c 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -1,7 +1,7 @@ import pytest from datetime import datetime, timezone from babel.dates import get_timezone - +from babel.core import UnknownLocaleError from mkdocs_git_revision_date_localized_plugin.dates import get_date_formats @@ -18,6 +18,37 @@ def test_get_dates(): } assert get_date_formats(0) == expected_output + # Test with 4-letter locale + new_expected_output = expected_output.copy() + new_expected_output["timeago"] = '' + assert get_date_formats(0, locale="en_US") == new_expected_output + + # Test with different locale + expected_output = { + "date": "1 janvier 1970", + "datetime": "1 janvier 1970 00:00:00", + "iso_date": "1970-01-01", + "iso_datetime": "1970-01-01 00:00:00", + "timeago": '', + "custom": "01. janvier 1970" + } + assert get_date_formats(0, locale="fr") == expected_output + + # Test with pt_BR locale + expected_output = { + "date": "1 de janeiro de 1970", + "datetime": "1 de janeiro de 1970 00:00:00", + "iso_date": "1970-01-01", + "iso_datetime": "1970-01-01 00:00:00", + "timeago": '', + "custom": "01. janeiro 1970" + } + assert get_date_formats(0, locale="pt_BR") == expected_output + + # Test with non-existing locale + with pytest.raises(UnknownLocaleError): + get_date_formats(0, locale="abcd") + # Test with custom arguments expected_output = { "date": "January 1, 1970", From c332bedac85350ff4a3e7444434ea7ef7ac8a43a Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 20:26:01 +0000 Subject: [PATCH 21/44] Enable parallel processing --- docs/options.md | 12 ++++ .../plugin.py | 65 ++++++++++++++++--- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/docs/options.md b/docs/options.md index c0835d6..613753a 100644 --- a/docs/options.md +++ b/docs/options.md @@ -177,3 +177,15 @@ git `blame.ignoreRevsFile`. This can be useful to ignore specific commits that a - git-revision-date-localized: ignored_commits_file: .git-blame-ignore-revs ``` + +## `enable_parallel_processing` + +Default is `true`. When enabled, the plugin will use `multiprocessing` to iterate through all site files in parallel. Disable if you encounter any errors (and open an issue!). + +=== ":octicons-file-code-16: mkdocs.yml" + + ```yaml + plugins: + - git-revision-date-localized: + enable_parallel_processing: True + ``` diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 49747da..7131c96 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -9,6 +9,7 @@ import re import os import time +import multiprocessing from mkdocs import __version__ as mkdocs_version from mkdocs.config import config_options @@ -16,6 +17,8 @@ from mkdocs.structure.nav import Page from mkdocs.utils import copy_file from mkdocs.exceptions import ConfigurationError +from mkdocs.config.defaults import MkDocsConfig +from mkdocs.structure.files import Files from mkdocs_git_revision_date_localized_plugin.util import Util from mkdocs_git_revision_date_localized_plugin.exclude import exclude @@ -47,8 +50,14 @@ class GitRevisionDateLocalizedPlugin(BasePlugin): ("strict", config_options.Type(bool, default=True)), ("enable_git_follow", config_options.Type(bool, default=True)), ("ignored_commits_file", config_options.Type(str, default=None)), + ("enable_parallel_processing", config_options.Type(bool, default=True)), ) + def __init__(self): + super().__init__() + self.last_revision_commits = {} + self.created_commits = {} + def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: """ Determine which locale to use. @@ -135,6 +144,42 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: return config + + def parallel_compute_commit_timestamps(self, files, docs_dir, is_first_commit=False): + pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count())) + results = [] + for file in files: + if file.is_documentation_page(): + abs_src_path = os.path.join(docs_dir, file.src_uri) + result = pool.apply_async( + self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit) + ) + results.append((abs_src_path, result)) + pool.close() + pool.join() + if is_first_commit: + for src_uri, result in results: + self.created_commits[src_uri] = result.get() + else: + for src_uri, result in results: + self.last_revision_commits[src_uri] = result.get() + + def on_files(self, files: Files, config: MkDocsConfig): + """ + Compute commit timestamps for all files in parallel. + """ + if not self.config.get("enabled") or not self.config.get("enable_parallel_processing"): + return + # Some plugins like TechDocs/monorepo copies docs_dir to a tmp dir and we need the real git path. + real_docs_dir = os.path.join( + os.path.dirname(config["config_file_path"]), "docs" + ) + if not self.last_revision_commits: + self.parallel_compute_commit_timestamps(files=files, docs_dir=real_docs_dir, is_first_commit=False) + if not self.created_commits: + self.parallel_compute_commit_timestamps(files=files, docs_dir=real_docs_dir, is_first_commit=True) + + def on_page_markdown(self, markdown: str, page: Page, config: config_options.Config, files, **kwargs) -> str: """ Replace jinja2 tags in markdown and templates with the localized dates. @@ -189,10 +234,12 @@ 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.util.get_git_commit_timestamp( - path=page.file.abs_src_path, - is_first_commit=False, - ) + last_revision_hash, last_revision_timestamp = self.last_revision_commits.get(page.file.abs_src_path, (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, + ) # Last revision date revision_dates = self.util.get_date_formats_for_timestamp( @@ -261,10 +308,12 @@ 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.util.get_git_commit_timestamp( - path=page.file.abs_src_path, - is_first_commit=True, - ) + first_revision_hash, first_revision_timestamp = self.created_commits.get(page.file.abs_src_path, (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, + ) if first_revision_timestamp > last_revision_timestamp: # See also https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/111 From 7e248a1f39bc7181db196726b446de05acdad91b Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 21:11:37 +0000 Subject: [PATCH 22/44] Upgrade to uv --- .github/workflows/pythonpublish.yml | 16 +- .github/workflows/scheduled_unittests.yml | 3 +- .github/workflows/unittests.yml | 3 +- .github/workflows/unittests_codecov.yml | 3 +- CONTRIBUTING.md | 16 +- MANIFEST.in | 1 - pyproject.toml | 24 +- requirements.txt | 4 - requirements_dev.txt | 8 - uv.lock | 1204 +++++++++++++++++++++ 10 files changed, 1239 insertions(+), 43 deletions(-) delete mode 100644 requirements.txt delete mode 100644 requirements_dev.txt create mode 100644 uv.lock diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index ea26e3f..90ab87e 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -24,23 +24,22 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.x' + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine + uv pip install --upgrade pip + uv pip install setuptools wheel twine - name: Make sure unit tests succeed run: | git config --global user.name "Github Action" git config --global user.email "githubaction@gmail.com" - pip install -r requirements_dev.txt - pip install . - pytest + uv run pytest - name: Build run: | - pip install build setuptools - python -m build + uv build # See https://docs.pypi.org/trusted-publishers/using-a-publisher/ - name: Publish package distributions to PyPI @@ -48,5 +47,4 @@ jobs: - name: Deploy mkdocs site run: | - pip install -r requirements_dev.txt - mkdocs gh-deploy --force + uv run mkdocs gh-deploy --force diff --git a/.github/workflows/scheduled_unittests.yml b/.github/workflows/scheduled_unittests.yml index 58dd70c..1aaa8bb 100644 --- a/.github/workflows/scheduled_unittests.yml +++ b/.github/workflows/scheduled_unittests.yml @@ -27,8 +27,7 @@ jobs: pip3 install --upgrade pip pip3 install --upgrade setuptools pip3 install --upgrade wheel - pip3 install -r requirements_dev.txt - pip3 install . + pip3 install ".[dev]" - name: Run unit tests run: | diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 6d0d68c..6f59119 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -23,8 +23,7 @@ jobs: pip3 install --upgrade pip pip3 install --upgrade setuptools pip3 install --upgrade wheel - pip3 install -r requirements_dev.txt - pip3 install . + pip3 install ".[dev]" - name: Run unit tests run: | diff --git a/.github/workflows/unittests_codecov.yml b/.github/workflows/unittests_codecov.yml index 63904b4..b1e0f78 100644 --- a/.github/workflows/unittests_codecov.yml +++ b/.github/workflows/unittests_codecov.yml @@ -31,8 +31,7 @@ jobs: pip3 install --upgrade pip pip3 install --upgrade setuptools pip3 install --upgrade wheel - pip install -r requirements_dev.txt - pip3 install . + pip3 install ".[dev]" - name: Generate coverage report run: | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21ed37c..b1b9d30 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,14 +7,16 @@ Thanks for considering to contribute to this project! Some guidelines: - This package tries to be as simple as possible for the user (hide any complexity from the user). New options are only added when there is clear value to the majority of users. - When issues or pull requests are not going to be resolved or merged, they should be closed as soon as possible. This is kinder than deciding this after a long period. Our issue tracker should reflect work to be done. +## Setup + +We use [uv to manage dependencies](https://docs.astral.sh/uv/getting-started/installation/). + ## Unit Tests -Make sure to install an editable version before running the tests: +To run the unit tests, use: ```python -pip install -r requirements_dev.txt -pip install -e . -pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report term-missing tests/ +uv run pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report term-missing tests/ ``` If it makes sense, writing tests for your PRs is always appreciated and will help get them merged. @@ -25,9 +27,7 @@ To quickly serve a test website with your latest changes to the plugin use the s For example: ```python -pip install -r tests/test_requirements.txt -pip install -e . -mkdocs serve -f tests/fixtures/basic_project/mkdocs.yml +uv run mkdocs serve -f tests/fixtures/basic_project/mkdocs.yml ``` ## Code Style @@ -35,4 +35,6 @@ mkdocs serve -f tests/fixtures/basic_project/mkdocs.yml Make sure your code *roughly* follows [PEP-8](https://www.python.org/dev/peps/pep-0008/) and keeps things consistent with the rest of the code. +We run `ruff format` to automatically style the code. + We use Google-style docstrings. diff --git a/MANIFEST.in b/MANIFEST.in index c84efdc..1a03abb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,2 @@ -include requirements.txt include mkdocs_git_revision_date_localized_plugin/js/*.js include mkdocs_git_revision_date_localized_plugin/css/*.css diff --git a/pyproject.toml b/pyproject.toml index 46e2a74..c5f979d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,13 @@ classifiers=[ "License :: OSI Approved :: MIT License", ] -dynamic = ["version","dependencies","optional-dependencies"] +dynamic = ["version"] +dependencies = [ + "babel>=2.7.0", + "gitpython>=3.1.44", + "mkdocs>=1.0", + "pytz>=2025.1", +] [project.urls] "Homepage" = "https://github.com/timvink/mkdocs-git-revision-date-localized-plugin" @@ -39,12 +45,6 @@ dynamic = ["version","dependencies","optional-dependencies"] [tool.setuptools.dynamic] version = {attr = "mkdocs_git_revision_date_localized_plugin.__version__"} -dependencies={file = ["requirements.txt"]} - -optional-dependencies.dev={file = ["requirements_dev.txt"]} -optional-dependencies.base={file = ["requirements.txt"]} -optional-dependencies.all={file = ["requirements.txt", "requirements_dev.txt"]} - [tool.pytest.ini_options] markers = [ "serial", @@ -114,5 +114,13 @@ fix = true [tool.uv] dev-dependencies = [ + "click>=8.1.8", + "codecov>=2.1.13", + "mkdocs-gen-files>=0.5.0", + "mkdocs-git-authors-plugin>=0.9.2", + "mkdocs-material>=9.6.7", + "mkdocs-static-i18n>=1.3.0", + "pytest>=8.3.5", + "pytest-cov>=5.0.0", "ruff", -] \ No newline at end of file +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 45ba908..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -mkdocs>=1.0 -GitPython -babel>=2.7.0 -pytz \ No newline at end of file diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index 66f09e3..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,8 +0,0 @@ -pytest -pytest-cov -codecov -click -mkdocs-material -mkdocs-static-i18n -mkdocs-gen-files -mkdocs-git-authors-plugin \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..f62107f --- /dev/null +++ b/uv.lock @@ -0,0 +1,1204 @@ +version = 1 +revision = 1 +requires-python = ">=3.8" +resolution-markers = [ + "python_full_version >= '3.9'", + "python_full_version < '3.9'", +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytz", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, +] + +[[package]] +name = "backrefs" +version = "5.7.post1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/df/30/903f35159c87ff1d92aa3fcf8cb52de97632a21e0ae43ed940f5d033e01a/backrefs-5.7.post1.tar.gz", hash = "sha256:8b0f83b770332ee2f1c8244f4e03c77d127a0fa529328e6a0e77fa25bee99678", size = 6582270 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/bb/47fc255d1060dcfd55b460236380edd8ebfc5b2a42a0799ca90c9fc983e3/backrefs-5.7.post1-py310-none-any.whl", hash = "sha256:c5e3fd8fd185607a7cb1fefe878cfb09c34c0be3c18328f12c574245f1c0287e", size = 380429 }, + { url = "https://files.pythonhosted.org/packages/89/72/39ef491caef3abae945f5a5fd72830d3b596bfac0630508629283585e213/backrefs-5.7.post1-py311-none-any.whl", hash = "sha256:712ea7e494c5bf3291156e28954dd96d04dc44681d0e5c030adf2623d5606d51", size = 392234 }, + { url = "https://files.pythonhosted.org/packages/6a/00/33403f581b732ca70fdebab558e8bbb426a29c34e0c3ed674a479b74beea/backrefs-5.7.post1-py312-none-any.whl", hash = "sha256:a6142201c8293e75bce7577ac29e1a9438c12e730d73a59efdd1b75528d1a6c5", size = 398110 }, + { url = "https://files.pythonhosted.org/packages/5d/ea/df0ac74a26838f6588aa012d5d801831448b87d0a7d0aefbbfabbe894870/backrefs-5.7.post1-py38-none-any.whl", hash = "sha256:ec61b1ee0a4bfa24267f6b67d0f8c5ffdc8e0d7dc2f18a2685fd1d8d9187054a", size = 369477 }, + { url = "https://files.pythonhosted.org/packages/6f/e8/e43f535c0a17a695e5768670fc855a0e5d52dc0d4135b3915bfa355f65ac/backrefs-5.7.post1-py39-none-any.whl", hash = "sha256:05c04af2bf752bb9a6c9dcebb2aff2fab372d3d9d311f2a138540e307756bd3a", size = 380429 }, +] + +[[package]] +name = "backrefs" +version = "5.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/46/caba1eb32fa5784428ab401a5487f73db4104590ecd939ed9daaf18b47e0/backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd", size = 6773994 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/cb/d019ab87fe70e0fe3946196d50d6a4428623dc0c38a6669c8cae0320fbf3/backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d", size = 380337 }, + { url = "https://files.pythonhosted.org/packages/a9/86/abd17f50ee21b2248075cb6924c6e7f9d23b4925ca64ec660e869c2633f1/backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b", size = 392142 }, + { url = "https://files.pythonhosted.org/packages/b3/04/7b415bd75c8ab3268cc138c76fa648c19495fcc7d155508a0e62f3f82308/backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486", size = 398021 }, + { url = "https://files.pythonhosted.org/packages/04/b8/60dcfb90eb03a06e883a92abbc2ab95c71f0d8c9dd0af76ab1d5ce0b1402/backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585", size = 399915 }, + { url = "https://files.pythonhosted.org/packages/0c/37/fb6973edeb700f6e3d6ff222400602ab1830446c25c7b4676d8de93e65b8/backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc", size = 380336 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, + { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, + { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, + { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, + { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, + { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, + { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, + { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, + { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, + { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, + { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, + { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, + { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, + { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, + { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, + { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, + { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, + { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, + { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, + { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, + { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, + { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, + { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, + { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, + { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, + { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/10/bd/6517ea94f2672e801011d50b5d06be2a0deaf566aea27bcdcd47e5195357/charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", size = 195653 }, + { url = "https://files.pythonhosted.org/packages/e5/0d/815a2ba3f283b4eeaa5ece57acade365c5b4135f65a807a083c818716582/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", size = 140701 }, + { url = "https://files.pythonhosted.org/packages/aa/17/c94be7ee0d142687e047fe1de72060f6d6837f40eedc26e87e6e124a3fc6/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", size = 150495 }, + { url = "https://files.pythonhosted.org/packages/f7/33/557ac796c47165fc141e4fb71d7b0310f67e05cb420756f3a82e0a0068e0/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", size = 142946 }, + { url = "https://files.pythonhosted.org/packages/1e/0d/38ef4ae41e9248d63fc4998d933cae22473b1b2ac4122cf908d0f5eb32aa/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", size = 144737 }, + { url = "https://files.pythonhosted.org/packages/43/01/754cdb29dd0560f58290aaaa284d43eea343ad0512e6ad3b8b5c11f08592/charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", size = 147471 }, + { url = "https://files.pythonhosted.org/packages/ba/cd/861883ba5160c7a9bd242c30b2c71074cda2aefcc0addc91118e0d4e0765/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", size = 140801 }, + { url = "https://files.pythonhosted.org/packages/6f/7f/0c0dad447819e90b93f8ed238cc8f11b91353c23c19e70fa80483a155bed/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", size = 149312 }, + { url = "https://files.pythonhosted.org/packages/8e/09/9f8abcc6fff60fb727268b63c376c8c79cc37b833c2dfe1f535dfb59523b/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", size = 152347 }, + { url = "https://files.pythonhosted.org/packages/be/e5/3f363dad2e24378f88ccf63ecc39e817c29f32e308ef21a7a6d9c1201165/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", size = 149888 }, + { url = "https://files.pythonhosted.org/packages/e4/10/a78c0e91f487b4ad0ef7480ac765e15b774f83de2597f1b6ef0eaf7a2f99/charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", size = 145169 }, + { url = "https://files.pythonhosted.org/packages/d3/81/396e7d7f5d7420da8273c91175d2e9a3f569288e3611d521685e4b9ac9cc/charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", size = 95094 }, + { url = "https://files.pythonhosted.org/packages/40/bb/20affbbd9ea29c71ea123769dc568a6d42052ff5089c5fe23e21e21084a6/charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", size = 102139 }, + { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 }, + { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 }, + { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 }, + { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 }, + { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 }, + { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 }, + { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 }, + { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 }, + { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 }, + { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 }, + { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 }, + { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 }, + { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "codecov" +version = "2.1.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", version = "7.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "coverage", version = "7.6.12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/bb/594b26d2c85616be6195a64289c578662678afa4910cef2d3ce8417cf73e/codecov-2.1.13.tar.gz", hash = "sha256:2362b685633caeaf45b9951a9b76ce359cd3581dd515b430c6c3f5dfb4d92a8c", size = 21416 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/02/18785edcdf6266cdd6c6dc7635f1cbeefd9a5b4c3bb8aff8bd681e9dd095/codecov-2.1.13-py2.py3-none-any.whl", hash = "sha256:c2ca5e51bba9ebb43644c43d0690148a55086f7f5e6fd36170858fa4206744d5", size = 16512 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "coverage" +version = "7.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/61/eb7ce5ed62bacf21beca4937a90fe32545c91a3c8a42a30c6616d48fc70d/coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", size = 206690 }, + { url = "https://files.pythonhosted.org/packages/7d/73/041928e434442bd3afde5584bdc3f932fb4562b1597629f537387cec6f3d/coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", size = 207127 }, + { url = "https://files.pythonhosted.org/packages/c7/c8/6ca52b5147828e45ad0242388477fdb90df2c6cbb9a441701a12b3c71bc8/coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", size = 235654 }, + { url = "https://files.pythonhosted.org/packages/d5/da/9ac2b62557f4340270942011d6efeab9833648380109e897d48ab7c1035d/coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc", size = 233598 }, + { url = "https://files.pythonhosted.org/packages/53/23/9e2c114d0178abc42b6d8d5281f651a8e6519abfa0ef460a00a91f80879d/coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", size = 234732 }, + { url = "https://files.pythonhosted.org/packages/0f/7e/a0230756fb133343a52716e8b855045f13342b70e48e8ad41d8a0d60ab98/coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", size = 233816 }, + { url = "https://files.pythonhosted.org/packages/28/7c/3753c8b40d232b1e5eeaed798c875537cf3cb183fb5041017c1fdb7ec14e/coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", size = 232325 }, + { url = "https://files.pythonhosted.org/packages/57/e3/818a2b2af5b7573b4b82cf3e9f137ab158c90ea750a8f053716a32f20f06/coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", size = 233418 }, + { url = "https://files.pythonhosted.org/packages/c8/fb/4532b0b0cefb3f06d201648715e03b0feb822907edab3935112b61b885e2/coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", size = 209343 }, + { url = "https://files.pythonhosted.org/packages/5a/25/af337cc7421eca1c187cc9c315f0a755d48e755d2853715bfe8c418a45fa/coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", size = 210136 }, + { url = "https://files.pythonhosted.org/packages/ad/5f/67af7d60d7e8ce61a4e2ddcd1bd5fb787180c8d0ae0fbd073f903b3dd95d/coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", size = 206796 }, + { url = "https://files.pythonhosted.org/packages/e1/0e/e52332389e057daa2e03be1fbfef25bb4d626b37d12ed42ae6281d0a274c/coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", size = 207244 }, + { url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279 }, + { url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859 }, + { url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549 }, + { url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477 }, + { url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134 }, + { url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910 }, + { url = "https://files.pythonhosted.org/packages/7d/30/033e663399ff17dca90d793ee8a2ea2890e7fdf085da58d82468b4220bf7/coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", size = 209348 }, + { url = "https://files.pythonhosted.org/packages/20/05/0d1ccbb52727ccdadaa3ff37e4d2dc1cd4d47f0c3df9eb58d9ec8508ca88/coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", size = 210230 }, + { url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983 }, + { url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221 }, + { url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 }, + { url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 }, + { url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 }, + { url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 }, + { url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 }, + { url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 }, + { url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606 }, + { url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373 }, + { url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007 }, + { url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269 }, + { url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 }, + { url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 }, + { url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 }, + { url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 }, + { url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 }, + { url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 }, + { url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671 }, + { url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368 }, + { url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758 }, + { url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035 }, + { url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 }, + { url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 }, + { url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 }, + { url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 }, + { url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 }, + { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 }, + { url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 }, + { url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 }, + { url = "https://files.pythonhosted.org/packages/81/d0/d9e3d554e38beea5a2e22178ddb16587dbcbe9a1ef3211f55733924bf7fa/coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0", size = 206674 }, + { url = "https://files.pythonhosted.org/packages/38/ea/cab2dc248d9f45b2b7f9f1f596a4d75a435cb364437c61b51d2eb33ceb0e/coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a", size = 207101 }, + { url = "https://files.pythonhosted.org/packages/ca/6f/f82f9a500c7c5722368978a5390c418d2a4d083ef955309a8748ecaa8920/coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b", size = 236554 }, + { url = "https://files.pythonhosted.org/packages/a6/94/d3055aa33d4e7e733d8fa309d9adf147b4b06a82c1346366fc15a2b1d5fa/coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3", size = 234440 }, + { url = "https://files.pythonhosted.org/packages/e4/6e/885bcd787d9dd674de4a7d8ec83faf729534c63d05d51d45d4fa168f7102/coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de", size = 235889 }, + { url = "https://files.pythonhosted.org/packages/f4/63/df50120a7744492710854860783d6819ff23e482dee15462c9a833cc428a/coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6", size = 235142 }, + { url = "https://files.pythonhosted.org/packages/3a/5d/9d0acfcded2b3e9ce1c7923ca52ccc00c78a74e112fc2aee661125b7843b/coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569", size = 233805 }, + { url = "https://files.pythonhosted.org/packages/c4/56/50abf070cb3cd9b1dd32f2c88f083aab561ecbffbcd783275cb51c17f11d/coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989", size = 234655 }, + { url = "https://files.pythonhosted.org/packages/25/ee/b4c246048b8485f85a2426ef4abab88e48c6e80c74e964bea5cd4cd4b115/coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7", size = 209296 }, + { url = "https://files.pythonhosted.org/packages/5c/1c/96cf86b70b69ea2b12924cdf7cabb8ad10e6130eab8d767a1099fbd2a44f/coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8", size = 210137 }, + { url = "https://files.pythonhosted.org/packages/19/d3/d54c5aa83268779d54c86deb39c1c4566e5d45c155369ca152765f8db413/coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", size = 206688 }, + { url = "https://files.pythonhosted.org/packages/a5/fe/137d5dca72e4a258b1bc17bb04f2e0196898fe495843402ce826a7419fe3/coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", size = 207120 }, + { url = "https://files.pythonhosted.org/packages/78/5b/a0a796983f3201ff5485323b225d7c8b74ce30c11f456017e23d8e8d1945/coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", size = 235249 }, + { url = "https://files.pythonhosted.org/packages/4e/e1/76089d6a5ef9d68f018f65411fcdaaeb0141b504587b901d74e8587606ad/coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", size = 233237 }, + { url = "https://files.pythonhosted.org/packages/9a/6f/eef79b779a540326fee9520e5542a8b428cc3bfa8b7c8f1022c1ee4fc66c/coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", size = 234311 }, + { url = "https://files.pythonhosted.org/packages/75/e1/656d65fb126c29a494ef964005702b012f3498db1a30dd562958e85a4049/coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", size = 233453 }, + { url = "https://files.pythonhosted.org/packages/68/6a/45f108f137941a4a1238c85f28fd9d048cc46b5466d6b8dda3aba1bb9d4f/coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", size = 231958 }, + { url = "https://files.pythonhosted.org/packages/9b/e7/47b809099168b8b8c72ae311efc3e88c8d8a1162b3ba4b8da3cfcdb85743/coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", size = 232938 }, + { url = "https://files.pythonhosted.org/packages/52/80/052222ba7058071f905435bad0ba392cc12006380731c37afaf3fe749b88/coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", size = 209352 }, + { url = "https://files.pythonhosted.org/packages/b8/d8/1b92e0b3adcf384e98770a00ca095da1b5f7b483e6563ae4eb5e935d24a1/coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", size = 210153 }, + { url = "https://files.pythonhosted.org/packages/a5/2b/0354ed096bca64dc8e32a7cbcae28b34cb5ad0b1fe2125d6d99583313ac0/coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", size = 198926 }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version < '3.9'" }, +] + +[[package]] +name = "coverage" +version = "7.6.12" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/d6/2b53ab3ee99f2262e6f0b8369a43f6d66658eab45510331c0b3d5c8c4272/coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", size = 805941 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/67/81dc41ec8f548c365d04a29f1afd492d3176b372c33e47fa2a45a01dc13a/coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8", size = 208345 }, + { url = "https://files.pythonhosted.org/packages/33/43/17f71676016c8829bde69e24c852fef6bd9ed39f774a245d9ec98f689fa0/coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879", size = 208775 }, + { url = "https://files.pythonhosted.org/packages/86/25/c6ff0775f8960e8c0840845b723eed978d22a3cd9babd2b996e4a7c502c6/coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe", size = 237925 }, + { url = "https://files.pythonhosted.org/packages/b0/3d/5f5bd37046243cb9d15fff2c69e498c2f4fe4f9b42a96018d4579ed3506f/coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674", size = 235835 }, + { url = "https://files.pythonhosted.org/packages/b5/f1/9e6b75531fe33490b910d251b0bf709142e73a40e4e38a3899e6986fe088/coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb", size = 236966 }, + { url = "https://files.pythonhosted.org/packages/4f/bc/aef5a98f9133851bd1aacf130e754063719345d2fb776a117d5a8d516971/coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c", size = 236080 }, + { url = "https://files.pythonhosted.org/packages/eb/d0/56b4ab77f9b12aea4d4c11dc11cdcaa7c29130b837eb610639cf3400c9c3/coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c", size = 234393 }, + { url = "https://files.pythonhosted.org/packages/0d/77/28ef95c5d23fe3dd191a0b7d89c82fea2c2d904aef9315daf7c890e96557/coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e", size = 235536 }, + { url = "https://files.pythonhosted.org/packages/29/62/18791d3632ee3ff3f95bc8599115707d05229c72db9539f208bb878a3d88/coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425", size = 211063 }, + { url = "https://files.pythonhosted.org/packages/fc/57/b3878006cedfd573c963e5c751b8587154eb10a61cc0f47a84f85c88a355/coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa", size = 211955 }, + { url = "https://files.pythonhosted.org/packages/64/2d/da78abbfff98468c91fd63a73cccdfa0e99051676ded8dd36123e3a2d4d5/coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015", size = 208464 }, + { url = "https://files.pythonhosted.org/packages/31/f2/c269f46c470bdabe83a69e860c80a82e5e76840e9f4bbd7f38f8cebbee2f/coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45", size = 208893 }, + { url = "https://files.pythonhosted.org/packages/47/63/5682bf14d2ce20819998a49c0deadb81e608a59eed64d6bc2191bc8046b9/coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702", size = 241545 }, + { url = "https://files.pythonhosted.org/packages/6a/b6/6b6631f1172d437e11067e1c2edfdb7238b65dff965a12bce3b6d1bf2be2/coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0", size = 239230 }, + { url = "https://files.pythonhosted.org/packages/c7/01/9cd06cbb1be53e837e16f1b4309f6357e2dfcbdab0dd7cd3b1a50589e4e1/coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f", size = 241013 }, + { url = "https://files.pythonhosted.org/packages/4b/26/56afefc03c30871326e3d99709a70d327ac1f33da383cba108c79bd71563/coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f", size = 239750 }, + { url = "https://files.pythonhosted.org/packages/dd/ea/88a1ff951ed288f56aa561558ebe380107cf9132facd0b50bced63ba7238/coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d", size = 238462 }, + { url = "https://files.pythonhosted.org/packages/6e/d4/1d9404566f553728889409eff82151d515fbb46dc92cbd13b5337fa0de8c/coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba", size = 239307 }, + { url = "https://files.pythonhosted.org/packages/12/c1/e453d3b794cde1e232ee8ac1d194fde8e2ba329c18bbf1b93f6f5eef606b/coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f", size = 211117 }, + { url = "https://files.pythonhosted.org/packages/d5/db/829185120c1686fa297294f8fcd23e0422f71070bf85ef1cc1a72ecb2930/coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558", size = 212019 }, + { url = "https://files.pythonhosted.org/packages/e2/7f/4af2ed1d06ce6bee7eafc03b2ef748b14132b0bdae04388e451e4b2c529b/coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", size = 208645 }, + { url = "https://files.pythonhosted.org/packages/dc/60/d19df912989117caa95123524d26fc973f56dc14aecdec5ccd7d0084e131/coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", size = 208898 }, + { url = "https://files.pythonhosted.org/packages/bd/10/fecabcf438ba676f706bf90186ccf6ff9f6158cc494286965c76e58742fa/coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", size = 242987 }, + { url = "https://files.pythonhosted.org/packages/4c/53/4e208440389e8ea936f5f2b0762dcd4cb03281a7722def8e2bf9dc9c3d68/coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", size = 239881 }, + { url = "https://files.pythonhosted.org/packages/c4/47/2ba744af8d2f0caa1f17e7746147e34dfc5f811fb65fc153153722d58835/coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", size = 242142 }, + { url = "https://files.pythonhosted.org/packages/e9/90/df726af8ee74d92ee7e3bf113bf101ea4315d71508952bd21abc3fae471e/coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", size = 241437 }, + { url = "https://files.pythonhosted.org/packages/f6/af/995263fd04ae5f9cf12521150295bf03b6ba940d0aea97953bb4a6db3e2b/coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", size = 239724 }, + { url = "https://files.pythonhosted.org/packages/1c/8e/5bb04f0318805e190984c6ce106b4c3968a9562a400180e549855d8211bd/coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", size = 241329 }, + { url = "https://files.pythonhosted.org/packages/9e/9d/fa04d9e6c3f6459f4e0b231925277cfc33d72dfab7fa19c312c03e59da99/coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", size = 211289 }, + { url = "https://files.pythonhosted.org/packages/53/40/53c7ffe3c0c3fff4d708bc99e65f3d78c129110d6629736faf2dbd60ad57/coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", size = 212079 }, + { url = "https://files.pythonhosted.org/packages/76/89/1adf3e634753c0de3dad2f02aac1e73dba58bc5a3a914ac94a25b2ef418f/coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1", size = 208673 }, + { url = "https://files.pythonhosted.org/packages/ce/64/92a4e239d64d798535c5b45baac6b891c205a8a2e7c9cc8590ad386693dc/coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd", size = 208945 }, + { url = "https://files.pythonhosted.org/packages/b4/d0/4596a3ef3bca20a94539c9b1e10fd250225d1dec57ea78b0867a1cf9742e/coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9", size = 242484 }, + { url = "https://files.pythonhosted.org/packages/1c/ef/6fd0d344695af6718a38d0861408af48a709327335486a7ad7e85936dc6e/coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e", size = 239525 }, + { url = "https://files.pythonhosted.org/packages/0c/4b/373be2be7dd42f2bcd6964059fd8fa307d265a29d2b9bcf1d044bcc156ed/coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4", size = 241545 }, + { url = "https://files.pythonhosted.org/packages/a6/7d/0e83cc2673a7790650851ee92f72a343827ecaaea07960587c8f442b5cd3/coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6", size = 241179 }, + { url = "https://files.pythonhosted.org/packages/ff/8c/566ea92ce2bb7627b0900124e24a99f9244b6c8c92d09ff9f7633eb7c3c8/coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3", size = 239288 }, + { url = "https://files.pythonhosted.org/packages/7d/e4/869a138e50b622f796782d642c15fb5f25a5870c6d0059a663667a201638/coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc", size = 241032 }, + { url = "https://files.pythonhosted.org/packages/ae/28/a52ff5d62a9f9e9fe9c4f17759b98632edd3a3489fce70154c7d66054dd3/coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3", size = 211315 }, + { url = "https://files.pythonhosted.org/packages/bc/17/ab849b7429a639f9722fa5628364c28d675c7ff37ebc3268fe9840dda13c/coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef", size = 212099 }, + { url = "https://files.pythonhosted.org/packages/d2/1c/b9965bf23e171d98505eb5eb4fb4d05c44efd256f2e0f19ad1ba8c3f54b0/coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e", size = 209511 }, + { url = "https://files.pythonhosted.org/packages/57/b3/119c201d3b692d5e17784fee876a9a78e1b3051327de2709392962877ca8/coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703", size = 209729 }, + { url = "https://files.pythonhosted.org/packages/52/4e/a7feb5a56b266304bc59f872ea07b728e14d5a64f1ad3a2cc01a3259c965/coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0", size = 253988 }, + { url = "https://files.pythonhosted.org/packages/65/19/069fec4d6908d0dae98126aa7ad08ce5130a6decc8509da7740d36e8e8d2/coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924", size = 249697 }, + { url = "https://files.pythonhosted.org/packages/1c/da/5b19f09ba39df7c55f77820736bf17bbe2416bbf5216a3100ac019e15839/coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b", size = 252033 }, + { url = "https://files.pythonhosted.org/packages/1e/89/4c2750df7f80a7872267f7c5fe497c69d45f688f7b3afe1297e52e33f791/coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d", size = 251535 }, + { url = "https://files.pythonhosted.org/packages/78/3b/6d3ae3c1cc05f1b0460c51e6f6dcf567598cbd7c6121e5ad06643974703c/coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827", size = 249192 }, + { url = "https://files.pythonhosted.org/packages/6e/8e/c14a79f535ce41af7d436bbad0d3d90c43d9e38ec409b4770c894031422e/coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9", size = 250627 }, + { url = "https://files.pythonhosted.org/packages/cb/79/b7cee656cfb17a7f2c1b9c3cee03dd5d8000ca299ad4038ba64b61a9b044/coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3", size = 212033 }, + { url = "https://files.pythonhosted.org/packages/b6/c3/f7aaa3813f1fa9a4228175a7bd368199659d392897e184435a3b66408dd3/coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f", size = 213240 }, + { url = "https://files.pythonhosted.org/packages/6c/eb/cf062b1c3dbdcafd64a2a154beea2e4aa8e9886c34e41f53fa04925c8b35/coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d", size = 208343 }, + { url = "https://files.pythonhosted.org/packages/95/42/4ebad0ab065228e29869a060644712ab1b0821d8c29bfefa20c2118c9e19/coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929", size = 208769 }, + { url = "https://files.pythonhosted.org/packages/44/9f/421e84f7f9455eca85ff85546f26cbc144034bb2587e08bfc214dd6e9c8f/coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87", size = 237553 }, + { url = "https://files.pythonhosted.org/packages/c9/c4/a2c4f274bcb711ed5db2ccc1b851ca1c45f35ed6077aec9d6c61845d80e3/coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c", size = 235473 }, + { url = "https://files.pythonhosted.org/packages/e0/10/a3d317e38e5627b06debe861d6c511b1611dd9dc0e2a47afbe6257ffd341/coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2", size = 236575 }, + { url = "https://files.pythonhosted.org/packages/4d/49/51cd991b56257d2e07e3d5cb053411e9de5b0f4e98047167ec05e4e19b55/coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd", size = 235690 }, + { url = "https://files.pythonhosted.org/packages/f7/87/631e5883fe0a80683a1f20dadbd0f99b79e17a9d8ea9aff3a9b4cfe50b93/coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73", size = 234040 }, + { url = "https://files.pythonhosted.org/packages/7c/34/edd03f6933f766ec97dddd178a7295855f8207bb708dbac03777107ace5b/coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86", size = 235048 }, + { url = "https://files.pythonhosted.org/packages/ee/1e/d45045b7d3012fe518c617a57b9f9396cdaebe6455f1b404858b32c38cdd/coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31", size = 211085 }, + { url = "https://files.pythonhosted.org/packages/df/ea/086cb06af14a84fe773b86aa140892006a906c5ec947e609ceb6a93f6257/coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57", size = 211965 }, + { url = "https://files.pythonhosted.org/packages/7a/7f/05818c62c7afe75df11e0233bd670948d68b36cdbf2a339a095bc02624a8/coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf", size = 200558 }, + { url = "https://files.pythonhosted.org/packages/fb/b2/f655700e1024dec98b10ebaafd0cedbc25e40e4abe62a3c8e2ceef4f8f0a/coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", size = 200552 }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version >= '3.9' and python_full_version <= '3.11'" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "zipp", version = "3.20.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +dependencies = [ + { name = "zipp", version = "3.21.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "jinja2" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", version = "2.1.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "markupsafe", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, +] + +[[package]] +name = "markdown" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", version = "8.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "importlib-metadata", version = "8.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/54/ad5eb37bf9d51800010a74e4665425831a9db4e7c4e0fde4352e391e808e/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", size = 18206 }, + { url = "https://files.pythonhosted.org/packages/6a/4a/a4d49415e600bacae038c67f9fecc1d5433b9d3c71a4de6f33537b89654c/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", size = 14079 }, + { url = "https://files.pythonhosted.org/packages/0a/7b/85681ae3c33c385b10ac0f8dd025c30af83c78cec1c37a6aa3b55e67f5ec/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", size = 26620 }, + { url = "https://files.pythonhosted.org/packages/7c/52/2b1b570f6b8b803cef5ac28fdf78c0da318916c7d2fe9402a84d591b394c/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", size = 25818 }, + { url = "https://files.pythonhosted.org/packages/29/fe/a36ba8c7ca55621620b2d7c585313efd10729e63ef81e4e61f52330da781/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", size = 25493 }, + { url = "https://files.pythonhosted.org/packages/60/ae/9c60231cdfda003434e8bd27282b1f4e197ad5a710c14bee8bea8a9ca4f0/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", size = 30630 }, + { url = "https://files.pythonhosted.org/packages/65/dc/1510be4d179869f5dafe071aecb3f1f41b45d37c02329dfba01ff59e5ac5/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", size = 29745 }, + { url = "https://files.pythonhosted.org/packages/30/39/8d845dd7d0b0613d86e0ef89549bfb5f61ed781f59af45fc96496e897f3a/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", size = 30021 }, + { url = "https://files.pythonhosted.org/packages/c7/5c/356a6f62e4f3c5fbf2602b4771376af22a3b16efa74eb8716fb4e328e01e/MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", size = 16659 }, + { url = "https://files.pythonhosted.org/packages/69/48/acbf292615c65f0604a0c6fc402ce6d8c991276e16c80c46a8f758fbd30c/MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", size = 17213 }, + { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219 }, + { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098 }, + { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014 }, + { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220 }, + { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756 }, + { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988 }, + { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718 }, + { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317 }, + { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670 }, + { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224 }, + { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 }, + { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 }, + { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 }, + { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 }, + { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 }, + { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 }, + { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 }, + { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 }, + { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 }, + { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 }, + { url = "https://files.pythonhosted.org/packages/f8/ff/2c942a82c35a49df5de3a630ce0a8456ac2969691b230e530ac12314364c/MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", size = 18192 }, + { url = "https://files.pythonhosted.org/packages/4f/14/6f294b9c4f969d0c801a4615e221c1e084722ea6114ab2114189c5b8cbe0/MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", size = 14072 }, + { url = "https://files.pythonhosted.org/packages/81/d4/fd74714ed30a1dedd0b82427c02fa4deec64f173831ec716da11c51a50aa/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", size = 26928 }, + { url = "https://files.pythonhosted.org/packages/c7/bd/50319665ce81bb10e90d1cf76f9e1aa269ea6f7fa30ab4521f14d122a3df/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", size = 26106 }, + { url = "https://files.pythonhosted.org/packages/4c/6f/f2b0f675635b05f6afd5ea03c094557bdb8622fa8e673387444fe8d8e787/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68", size = 25781 }, + { url = "https://files.pythonhosted.org/packages/51/e0/393467cf899b34a9d3678e78961c2c8cdf49fb902a959ba54ece01273fb1/MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", size = 30518 }, + { url = "https://files.pythonhosted.org/packages/f6/02/5437e2ad33047290dafced9df741d9efc3e716b75583bbd73a9984f1b6f7/MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", size = 29669 }, + { url = "https://files.pythonhosted.org/packages/0e/7d/968284145ffd9d726183ed6237c77938c021abacde4e073020f920e060b2/MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", size = 29933 }, + { url = "https://files.pythonhosted.org/packages/bf/f3/ecb00fc8ab02b7beae8699f34db9357ae49d9f21d4d3de6f305f34fa949e/MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", size = 16656 }, + { url = "https://files.pythonhosted.org/packages/92/21/357205f03514a49b293e214ac39de01fadd0970a6e05e4bf1ddd0ffd0881/MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", size = 17206 }, + { url = "https://files.pythonhosted.org/packages/0f/31/780bb297db036ba7b7bbede5e1d7f1e14d704ad4beb3ce53fb495d22bc62/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", size = 18193 }, + { url = "https://files.pythonhosted.org/packages/6c/77/d77701bbef72892affe060cdacb7a2ed7fd68dae3b477a8642f15ad3b132/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/d9/a7/1e558b4f78454c8a3a0199292d96159eb4d091f983bc35ef258314fe7269/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", size = 26486 }, + { url = "https://files.pythonhosted.org/packages/5f/5a/360da85076688755ea0cceb92472923086993e86b5613bbae9fbc14136b0/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", size = 25685 }, + { url = "https://files.pythonhosted.org/packages/6a/18/ae5a258e3401f9b8312f92b028c54d7026a97ec3ab20bfaddbdfa7d8cce8/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", size = 25338 }, + { url = "https://files.pythonhosted.org/packages/0b/cc/48206bd61c5b9d0129f4d75243b156929b04c94c09041321456fd06a876d/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", size = 30439 }, + { url = "https://files.pythonhosted.org/packages/d1/06/a41c112ab9ffdeeb5f77bc3e331fdadf97fa65e52e44ba31880f4e7f983c/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", size = 29531 }, + { url = "https://files.pythonhosted.org/packages/02/8c/ab9a463301a50dab04d5472e998acbd4080597abc048166ded5c7aa768c8/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", size = 29823 }, + { url = "https://files.pythonhosted.org/packages/bc/29/9bc18da763496b055d8e98ce476c8e718dcfd78157e17f555ce6dd7d0895/MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", size = 16658 }, + { url = "https://files.pythonhosted.org/packages/f6/f8/4da07de16f10551ca1f640c92b5f316f9394088b183c6a57183df6de5ae4/MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", size = 17211 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 }, + { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 }, + { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 }, + { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 }, + { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 }, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "importlib-metadata", version = "8.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "importlib-metadata", version = "8.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe", version = "2.1.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "markupsafe", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog", version = "4.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "watchdog", version = "6.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, +] + +[[package]] +name = "mkdocs-gen-files" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/85/2d634462fd59136197d3126ca431ffb666f412e3db38fd5ce3a60566303e/mkdocs_gen_files-0.5.0.tar.gz", hash = "sha256:4c7cf256b5d67062a788f6b1d035e157fc1a9498c2399be9af5257d4ff4d19bc", size = 7539 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/0f/1e55b3fd490ad2cecb6e7b31892d27cb9fc4218ec1dab780440ba8579e74/mkdocs_gen_files-0.5.0-py3-none-any.whl", hash = "sha256:7ac060096f3f40bd19039e7277dd3050be9a453c8ac578645844d4d91d7978ea", size = 8380 }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", version = "8.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "importlib-metadata", version = "8.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, +] + +[[package]] +name = "mkdocs-git-authors-plugin" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/ef/09ab7178d580e342cb3ba279c48eaf3abf55795a2ae6e5426fe2c725143c/mkdocs_git_authors_plugin-0.9.2.tar.gz", hash = "sha256:77f97c321e08a8757beb866293eb257070b11cd5a080976bc6696b249cbade4f", size = 21403 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/08/57d0fea1cc30096fcc94ec9cd4ccdee625be89fd710626f78d90fc13738e/mkdocs_git_authors_plugin-0.9.2-py3-none-any.whl", hash = "sha256:f6cefc4dc832865d26f7f9f944c0a8c7dc852742d79320f3800e0d97814e2a84", size = 20332 }, +] + +[[package]] +name = "mkdocs-git-revision-date-localized-plugin" +source = { editable = "." } +dependencies = [ + { name = "babel" }, + { name = "gitpython" }, + { name = "mkdocs" }, + { name = "pytz" }, +] + +[package.dev-dependencies] +dev = [ + { name = "click" }, + { name = "codecov" }, + { name = "mkdocs-gen-files" }, + { name = "mkdocs-git-authors-plugin" }, + { name = "mkdocs-material" }, + { name = "mkdocs-static-i18n" }, + { name = "pytest" }, + { name = "pytest-cov", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "pytest-cov", version = "6.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "babel", specifier = ">=2.7.0" }, + { name = "gitpython", specifier = ">=3.1.44" }, + { name = "mkdocs", specifier = ">=1.0" }, + { name = "pytz", specifier = ">=2025.1" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "click", specifier = ">=8.1.8" }, + { name = "codecov", specifier = ">=2.1.13" }, + { name = "mkdocs-gen-files", specifier = ">=0.5.0" }, + { name = "mkdocs-git-authors-plugin", specifier = ">=0.9.2" }, + { name = "mkdocs-material", specifier = ">=9.6.7" }, + { name = "mkdocs-static-i18n", specifier = ">=1.3.0" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-cov", specifier = ">=5.0.0" }, + { name = "ruff" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.6.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs", version = "5.7.post1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "backrefs", version = "5.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/d7/93e19c9587e5f4ed25647890555d58cf484a4d412be7037dc17b9c9179d9/mkdocs_material-9.6.7.tar.gz", hash = "sha256:3e2c1fceb9410056c2d91f334a00cdea3215c28750e00c691c1e46b2a33309b4", size = 3947458 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/d3/12f22de41bdd9e576ddc459b38c651d68edfb840b32acaa1f46ae36845e3/mkdocs_material-9.6.7-py3-none-any.whl", hash = "sha256:8a159e45e80fcaadd9fbeef62cbf928569b93df954d4dc5ba76d46820caf7b47", size = 8696755 }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, +] + +[[package]] +name = "mkdocs-static-i18n" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/2b/59652a2550465fde25ae6a009cb6d74d0f7e724d272fc952685807b29ca1/mkdocs_static_i18n-1.3.0.tar.gz", hash = "sha256:65731e1e4ec6d719693e24fee9340f5516460b2b7244d2a89bed4ce3cfa6a173", size = 1370450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/f7/ef222a7a2f96ecf79c7c00bfc9dde3b22cd2cc1bd2b7472c7b204fc64225/mkdocs_static_i18n-1.3.0-py3-none-any.whl", hash = "sha256:7905d52fff71d2c108b6c344fd223e848ca7e39ddf319b70864dfa47dba85d6b", size = 21660 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pymdown-extensions" +version = "10.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/44/e6de2fdc880ad0ec7547ca2e087212be815efbc9a425a8d5ba9ede602cbb/pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b", size = 846846 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/f5/b9e2a42aa8f9e34d52d66de87941ecd236570c7ed2e87775ed23bbe4e224/pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", size = 264467 }, +] + +[[package]] +name = "pytest" +version = "8.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, +] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "coverage", version = "7.6.1", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version < '3.9'" }, + { name = "pytest", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990 }, +] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +dependencies = [ + { name = "coverage", version = "7.6.12", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version >= '3.9'" }, + { name = "pytest", marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2025.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, + { url = "https://files.pythonhosted.org/packages/74/d9/323a59d506f12f498c2097488d80d16f4cf965cee1791eab58b56b19f47a/PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", size = 183218 }, + { url = "https://files.pythonhosted.org/packages/74/cc/20c34d00f04d785f2028737e2e2a8254e1425102e730fee1d6396f832577/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", size = 728067 }, + { url = "https://files.pythonhosted.org/packages/20/52/551c69ca1501d21c0de51ddafa8c23a0191ef296ff098e98358f69080577/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", size = 757812 }, + { url = "https://files.pythonhosted.org/packages/fd/7f/2c3697bba5d4aa5cc2afe81826d73dfae5f049458e44732c7a0938baa673/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", size = 746531 }, + { url = "https://files.pythonhosted.org/packages/8c/ab/6226d3df99900e580091bb44258fde77a8433511a86883bd4681ea19a858/PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", size = 800820 }, + { url = "https://files.pythonhosted.org/packages/a0/99/a9eb0f3e710c06c5d922026f6736e920d431812ace24aae38228d0d64b04/PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", size = 145514 }, + { url = "https://files.pythonhosted.org/packages/75/8a/ee831ad5fafa4431099aa4e078d4c8efd43cd5e48fbc774641d233b683a9/PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", size = 162702 }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3", version = "2.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "urllib3", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "ruff" +version = "0.9.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/418441a8170e8d53d05c0b9dad69760dbc7b8a12c10dbe6db1e1205d2377/ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933", size = 3717448 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/c3/2c4afa9ba467555d074b146d9aed0633a56ccdb900839fb008295d037b89/ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367", size = 10027252 }, + { url = "https://files.pythonhosted.org/packages/33/d1/439e58487cf9eac26378332e25e7d5ade4b800ce1eec7dc2cfc9b0d7ca96/ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7", size = 10840721 }, + { url = "https://files.pythonhosted.org/packages/50/44/fead822c38281ba0122f1b76b460488a175a9bd48b130650a6fb6dbcbcf9/ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d", size = 10161439 }, + { url = "https://files.pythonhosted.org/packages/11/ae/d404a2ab8e61ddf6342e09cc6b7f7846cce6b243e45c2007dbe0ca928a5d/ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a", size = 10336264 }, + { url = "https://files.pythonhosted.org/packages/6a/4e/7c268aa7d84cd709fb6f046b8972313142cffb40dfff1d2515c5e6288d54/ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe", size = 9908774 }, + { url = "https://files.pythonhosted.org/packages/cc/26/c618a878367ef1b76270fd027ca93692657d3f6122b84ba48911ef5f2edc/ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c", size = 11428127 }, + { url = "https://files.pythonhosted.org/packages/d7/9a/c5588a93d9bfed29f565baf193fe802fa676a0c837938137ea6cf0576d8c/ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be", size = 12133187 }, + { url = "https://files.pythonhosted.org/packages/3e/ff/e7980a7704a60905ed7e156a8d73f604c846d9bd87deda9cabfa6cba073a/ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590", size = 11602937 }, + { url = "https://files.pythonhosted.org/packages/24/78/3690444ad9e3cab5c11abe56554c35f005b51d1d118b429765249095269f/ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb", size = 13771698 }, + { url = "https://files.pythonhosted.org/packages/6e/bf/e477c2faf86abe3988e0b5fd22a7f3520e820b2ee335131aca2e16120038/ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0", size = 11249026 }, + { url = "https://files.pythonhosted.org/packages/f7/82/cdaffd59e5a8cb5b14c408c73d7a555a577cf6645faaf83e52fe99521715/ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17", size = 10220432 }, + { url = "https://files.pythonhosted.org/packages/fe/a4/2507d0026225efa5d4412b6e294dfe54725a78652a5c7e29e6bd0fc492f3/ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1", size = 9874602 }, + { url = "https://files.pythonhosted.org/packages/d5/be/f3aab1813846b476c4bcffe052d232244979c3cd99d751c17afb530ca8e4/ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57", size = 10851212 }, + { url = "https://files.pythonhosted.org/packages/8b/45/8e5fd559bea0d2f57c4e12bf197a2fade2fac465aa518284f157dfbca92b/ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e", size = 11327490 }, + { url = "https://files.pythonhosted.org/packages/42/55/e6c90f13880aeef327746052907e7e930681f26a164fe130ddac28b08269/ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1", size = 10227912 }, + { url = "https://files.pythonhosted.org/packages/35/b2/da925693cb82a1208aa34966c0f36cb222baca94e729dd22a587bc22d0f3/ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1", size = 11355632 }, + { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "watchdog" +version = "4.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/38/764baaa25eb5e35c9a043d4c4588f9836edfe52a708950f4b6d5f714fd42/watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270", size = 126587 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/b0/219893d41c16d74d0793363bf86df07d50357b81f64bba4cb94fe76e7af4/watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22", size = 100257 }, + { url = "https://files.pythonhosted.org/packages/6d/c6/8e90c65693e87d98310b2e1e5fd7e313266990853b489e85ce8396cc26e3/watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1", size = 92249 }, + { url = "https://files.pythonhosted.org/packages/6f/cd/2e306756364a934532ff8388d90eb2dc8bb21fe575cd2b33d791ce05a02f/watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503", size = 92888 }, + { url = "https://files.pythonhosted.org/packages/de/78/027ad372d62f97642349a16015394a7680530460b1c70c368c506cb60c09/watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930", size = 100256 }, + { url = "https://files.pythonhosted.org/packages/59/a9/412b808568c1814d693b4ff1cec0055dc791780b9dc947807978fab86bc1/watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b", size = 92252 }, + { url = "https://files.pythonhosted.org/packages/04/57/179d76076cff264982bc335dd4c7da6d636bd3e9860bbc896a665c3447b6/watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef", size = 92888 }, + { url = "https://files.pythonhosted.org/packages/92/f5/ea22b095340545faea37ad9a42353b265ca751f543da3fb43f5d00cdcd21/watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a", size = 100342 }, + { url = "https://files.pythonhosted.org/packages/cb/d2/8ce97dff5e465db1222951434e3115189ae54a9863aef99c6987890cc9ef/watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29", size = 92306 }, + { url = "https://files.pythonhosted.org/packages/49/c4/1aeba2c31b25f79b03b15918155bc8c0b08101054fc727900f1a577d0d54/watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a", size = 92915 }, + { url = "https://files.pythonhosted.org/packages/79/63/eb8994a182672c042d85a33507475c50c2ee930577524dd97aea05251527/watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b", size = 100343 }, + { url = "https://files.pythonhosted.org/packages/ce/82/027c0c65c2245769580605bcd20a1dc7dfd6c6683c8c4e2ef43920e38d27/watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d", size = 92313 }, + { url = "https://files.pythonhosted.org/packages/2a/89/ad4715cbbd3440cb0d336b78970aba243a33a24b1a79d66f8d16b4590d6a/watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7", size = 92919 }, + { url = "https://files.pythonhosted.org/packages/55/08/1a9086a3380e8828f65b0c835b86baf29ebb85e5e94a2811a2eb4f889cfd/watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040", size = 100255 }, + { url = "https://files.pythonhosted.org/packages/6c/3e/064974628cf305831f3f78264800bd03b3358ec181e3e9380a36ff156b93/watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7", size = 92257 }, + { url = "https://files.pythonhosted.org/packages/23/69/1d2ad9c12d93bc1e445baa40db46bc74757f3ffc3a3be592ba8dbc51b6e5/watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4", size = 92886 }, + { url = "https://files.pythonhosted.org/packages/68/eb/34d3173eceab490d4d1815ba9a821e10abe1da7a7264a224e30689b1450c/watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9", size = 100254 }, + { url = "https://files.pythonhosted.org/packages/18/a1/4bbafe7ace414904c2cc9bd93e472133e8ec11eab0b4625017f0e34caad8/watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578", size = 92249 }, + { url = "https://files.pythonhosted.org/packages/f3/11/ec5684e0ca692950826af0de862e5db167523c30c9cbf9b3f4ce7ec9cc05/watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b", size = 92891 }, + { url = "https://files.pythonhosted.org/packages/3b/9a/6f30f023324de7bad8a3eb02b0afb06bd0726003a3550e9964321315df5a/watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa", size = 91775 }, + { url = "https://files.pythonhosted.org/packages/87/62/8be55e605d378a154037b9ba484e00a5478e627b69c53d0f63e3ef413ba6/watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3", size = 92255 }, + { url = "https://files.pythonhosted.org/packages/6b/59/12e03e675d28f450bade6da6bc79ad6616080b317c472b9ae688d2495a03/watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508", size = 91682 }, + { url = "https://files.pythonhosted.org/packages/ef/69/241998de9b8e024f5c2fbdf4324ea628b4231925305011ca8b7e1c3329f6/watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee", size = 92249 }, + { url = "https://files.pythonhosted.org/packages/70/3f/2173b4d9581bc9b5df4d7f2041b6c58b5e5448407856f68d4be9981000d0/watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1", size = 91773 }, + { url = "https://files.pythonhosted.org/packages/f0/de/6fff29161d5789048f06ef24d94d3ddcc25795f347202b7ea503c3356acb/watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e", size = 92250 }, + { url = "https://files.pythonhosted.org/packages/8a/b1/25acf6767af6f7e44e0086309825bd8c098e301eed5868dc5350642124b9/watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83", size = 82947 }, + { url = "https://files.pythonhosted.org/packages/e8/90/aebac95d6f954bd4901f5d46dcd83d68e682bfd21798fd125a95ae1c9dbf/watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c", size = 82942 }, + { url = "https://files.pythonhosted.org/packages/15/3a/a4bd8f3b9381824995787488b9282aff1ed4667e1110f31a87b871ea851c/watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a", size = 82947 }, + { url = "https://files.pythonhosted.org/packages/09/cc/238998fc08e292a4a18a852ed8274159019ee7a66be14441325bcd811dfd/watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73", size = 82946 }, + { url = "https://files.pythonhosted.org/packages/80/f1/d4b915160c9d677174aa5fae4537ae1f5acb23b3745ab0873071ef671f0a/watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc", size = 82947 }, + { url = "https://files.pythonhosted.org/packages/db/02/56ebe2cf33b352fe3309588eb03f020d4d1c061563d9858a9216ba004259/watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757", size = 82944 }, + { url = "https://files.pythonhosted.org/packages/01/d2/c8931ff840a7e5bd5dcb93f2bb2a1fd18faf8312e9f7f53ff1cf76ecc8ed/watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8", size = 82947 }, + { url = "https://files.pythonhosted.org/packages/d0/d8/cdb0c21a4a988669d7c210c75c6a2c9a0e16a3b08d9f7e633df0d9a16ad8/watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19", size = 82935 }, + { url = "https://files.pythonhosted.org/packages/99/2e/b69dfaae7a83ea64ce36538cc103a3065e12c447963797793d5c0a1d5130/watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b", size = 82934 }, + { url = "https://files.pythonhosted.org/packages/b0/0b/43b96a9ecdd65ff5545b1b13b687ca486da5c6249475b1a45f24d63a1858/watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c", size = 82933 }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390 }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389 }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020 }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393 }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392 }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019 }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, + { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390 }, + { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386 }, + { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017 }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902 }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380 }, + { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903 }, + { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, +] + +[[package]] +name = "zipp" +version = "3.20.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/54/bf/5c0000c44ebc80123ecbdddba1f5dcd94a5ada602a9c225d84b5aaa55e86/zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", size = 24199 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", size = 9200 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +] From 4dba8b64316e905b7d8796984ba03de0ffabcaf4 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 21:13:36 +0000 Subject: [PATCH 23/44] update pythonpublish --- .github/workflows/pythonpublish.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 90ab87e..5014d48 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -26,10 +26,6 @@ jobs: python-version: '3.x' - name: Install uv uses: astral-sh/setup-uv@v5 - - name: Install dependencies - run: | - uv pip install --upgrade pip - uv pip install setuptools wheel twine - name: Make sure unit tests succeed run: | From e14b85ce6ca70ba2ec58d6658b9949c5f6e86de8 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 21:20:12 +0000 Subject: [PATCH 24/44] fix deploy action --- .github/workflows/pythonpublish.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 5014d48..97d990c 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -17,9 +17,6 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: '0' - sparse-checkout: | - docs - includes - name: Set up Python uses: actions/setup-python@v5 with: From f341e03a5144abd6a84e0624eff7d5136a872fd6 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 4 Mar 2025 21:25:02 +0000 Subject: [PATCH 25/44] use uv setup actions in github actions --- .github/workflows/scheduled_unittests.yml | 17 +++-------------- .github/workflows/unittests.yml | 13 +++---------- .github/workflows/unittests_codecov.yml | 13 +++---------- 3 files changed, 9 insertions(+), 34 deletions(-) diff --git a/.github/workflows/scheduled_unittests.yml b/.github/workflows/scheduled_unittests.yml index 1aaa8bb..0737445 100644 --- a/.github/workflows/scheduled_unittests.yml +++ b/.github/workflows/scheduled_unittests.yml @@ -16,22 +16,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - - - name: Setup Python - uses: actions/setup-python@master - with: - python-version: "3.12" - - - name: Install dependencies - run: | - pip3 install --upgrade pip - pip3 install --upgrade setuptools - pip3 install --upgrade wheel - pip3 install ".[dev]" - + - name: Install uv and set the python version + uses: astral-sh/setup-uv@v5 - name: Run unit tests run: | git config --global user.name "Github Action" git config --global user.email "githubaction@gmail.com" - pytest + uv run pytest \ No newline at end of file diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 6f59119..3703886 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -13,21 +13,14 @@ jobs: steps: - uses: actions/checkout@master - - name: Setup Python - uses: actions/setup-python@master + - name: Install uv and set the python version + uses: astral-sh/setup-uv@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip3 install --upgrade pip - pip3 install --upgrade setuptools - pip3 install --upgrade wheel - pip3 install ".[dev]" - - name: Run unit tests run: | git config --global user.name "Github Action" git config --global user.email "githubaction@gmail.com" - pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report=xml + uv run pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report=xml \ No newline at end of file diff --git a/.github/workflows/unittests_codecov.yml b/.github/workflows/unittests_codecov.yml index b1e0f78..b8cfab3 100644 --- a/.github/workflows/unittests_codecov.yml +++ b/.github/workflows/unittests_codecov.yml @@ -21,23 +21,16 @@ jobs: steps: - uses: actions/checkout@master - - name: Setup Python - uses: actions/setup-python@master + - name: Install uv and set the python version + uses: astral-sh/setup-uv@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip3 install --upgrade pip - pip3 install --upgrade setuptools - pip3 install --upgrade wheel - pip3 install ".[dev]" - - name: Generate coverage report run: | git config --global user.name "Github Action" git config --global user.email "githubaction@gmail.com" - pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report=xml + uv run pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report=xml - name: Upload coverage to Codecov if: "contains(env.USING_COVERAGE, matrix.python-version)" From 1da3a962d6dfa671fed7a868ea2bc9c15d7216f3 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Wed, 5 Mar 2025 09:10:54 +0000 Subject: [PATCH 26/44] Fix monorepo compatibility --- pyproject.toml | 1 + .../__init__.py | 2 +- .../plugin.py | 24 ++++++----- .../monorepo/components/a/docs/README.md | 1 + .../fixtures/monorepo/components/a/mkdocs.yml | 4 ++ .../monorepo/components/b/docs/README.md | 1 + .../fixtures/monorepo/components/b/mkdocs.yml | 4 ++ .../monorepo/components/c/docs/README.md | 1 + .../fixtures/monorepo/components/c/mkdocs.yml | 4 ++ .../fixtures/monorepo/components/d/README.md | 3 ++ .../monorepo/components/d/e/docs/README.md | 1 + .../monorepo/components/d/e/mkdocs.yml | 4 ++ tests/fixtures/monorepo/components/randomfile | 1 + .../fixtures/monorepo/docs/authentication.md | 10 +++++ tests/fixtures/monorepo/docs/index.md | 12 ++++++ tests/fixtures/monorepo/mkdocs.yml | 18 +++++++++ tests/fixtures/monorepo/v1/docs/changelog.md | 6 +++ tests/fixtures/monorepo/v1/docs/reference.md | 18 +++++++++ tests/fixtures/monorepo/v1/mkdocs.yml | 5 +++ tests/fixtures/monorepo/v2/docs/changelog.md | 6 +++ tests/fixtures/monorepo/v2/docs/migrating.md | 6 +++ tests/fixtures/monorepo/v2/docs/reference.md | 26 ++++++++++++ tests/fixtures/monorepo/v2/mkdocs.yml | 6 +++ tests/fixtures/monorepo/v3/docs/changelog.md | 6 +++ tests/fixtures/monorepo/v3/docs/migrating.md | 6 +++ tests/fixtures/monorepo/v3/docs/reference.md | 30 ++++++++++++++ tests/fixtures/monorepo/v3/mkdocs.yml | 3 ++ tests/test_builds.py | 40 ++++++++++++++----- uv.lock | 36 +++++++++++++++++ 29 files changed, 265 insertions(+), 20 deletions(-) create mode 100644 tests/fixtures/monorepo/components/a/docs/README.md create mode 100644 tests/fixtures/monorepo/components/a/mkdocs.yml create mode 100644 tests/fixtures/monorepo/components/b/docs/README.md create mode 100644 tests/fixtures/monorepo/components/b/mkdocs.yml create mode 100644 tests/fixtures/monorepo/components/c/docs/README.md create mode 100644 tests/fixtures/monorepo/components/c/mkdocs.yml create mode 100644 tests/fixtures/monorepo/components/d/README.md create mode 100644 tests/fixtures/monorepo/components/d/e/docs/README.md create mode 100644 tests/fixtures/monorepo/components/d/e/mkdocs.yml create mode 100644 tests/fixtures/monorepo/components/randomfile create mode 100644 tests/fixtures/monorepo/docs/authentication.md create mode 100644 tests/fixtures/monorepo/docs/index.md create mode 100644 tests/fixtures/monorepo/mkdocs.yml create mode 100644 tests/fixtures/monorepo/v1/docs/changelog.md create mode 100644 tests/fixtures/monorepo/v1/docs/reference.md create mode 100644 tests/fixtures/monorepo/v1/mkdocs.yml create mode 100644 tests/fixtures/monorepo/v2/docs/changelog.md create mode 100644 tests/fixtures/monorepo/v2/docs/migrating.md create mode 100644 tests/fixtures/monorepo/v2/docs/reference.md create mode 100644 tests/fixtures/monorepo/v2/mkdocs.yml create mode 100644 tests/fixtures/monorepo/v3/docs/changelog.md create mode 100644 tests/fixtures/monorepo/v3/docs/migrating.md create mode 100644 tests/fixtures/monorepo/v3/docs/reference.md create mode 100644 tests/fixtures/monorepo/v3/mkdocs.yml diff --git a/pyproject.toml b/pyproject.toml index c5f979d..d44d1c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,6 +119,7 @@ dev-dependencies = [ "mkdocs-gen-files>=0.5.0", "mkdocs-git-authors-plugin>=0.9.2", "mkdocs-material>=9.6.7", + "mkdocs-monorepo-plugin>=1.1.0", "mkdocs-static-i18n>=1.3.0", "pytest>=8.3.5", "pytest-cov>=5.0.0", diff --git a/src/mkdocs_git_revision_date_localized_plugin/__init__.py b/src/mkdocs_git_revision_date_localized_plugin/__init__.py index 3e8d9f9..bf25615 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/__init__.py +++ b/src/mkdocs_git_revision_date_localized_plugin/__init__.py @@ -1 +1 @@ -__version__ = "1.4.0" +__version__ = "1.4.1" diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 7131c96..126bee2 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -23,7 +23,7 @@ from mkdocs_git_revision_date_localized_plugin.util import Util from mkdocs_git_revision_date_localized_plugin.exclude import exclude -from typing import Any, Dict +from typing import Any, Dict, Optional from collections import OrderedDict from packaging.version import Version @@ -145,12 +145,15 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: return config - def parallel_compute_commit_timestamps(self, files, docs_dir, is_first_commit=False): + def parallel_compute_commit_timestamps(self, files, original_source: Optional[Dict] = None, is_first_commit=False): pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count())) results = [] for file in files: if file.is_documentation_page(): - abs_src_path = os.path.join(docs_dir, file.src_uri) + abs_src_path = file.abs_src_path + # Support plugins like monorep 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] result = pool.apply_async( self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit) ) @@ -170,14 +173,17 @@ def on_files(self, files: Files, config: MkDocsConfig): """ if not self.config.get("enabled") or not self.config.get("enable_parallel_processing"): return - # Some plugins like TechDocs/monorepo copies docs_dir to a tmp dir and we need the real git path. - real_docs_dir = os.path.join( - os.path.dirname(config["config_file_path"]), "docs" - ) + + # Support monorepo/techdocs, which copies the docs_dir to a temporary directory + if "monorepo" in config.get('plugins', {}): + original_source = config.get('plugins').get('monorepo').merger.files_source_dir + else: + original_source = None + if not self.last_revision_commits: - self.parallel_compute_commit_timestamps(files=files, docs_dir=real_docs_dir, is_first_commit=False) + self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=False) if not self.created_commits: - self.parallel_compute_commit_timestamps(files=files, docs_dir=real_docs_dir, is_first_commit=True) + self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=True) def on_page_markdown(self, markdown: str, page: Page, config: config_options.Config, files, **kwargs) -> str: diff --git a/tests/fixtures/monorepo/components/a/docs/README.md b/tests/fixtures/monorepo/components/a/docs/README.md new file mode 100644 index 0000000..b6e496d --- /dev/null +++ b/tests/fixtures/monorepo/components/a/docs/README.md @@ -0,0 +1 @@ +# Docs for A diff --git a/tests/fixtures/monorepo/components/a/mkdocs.yml b/tests/fixtures/monorepo/components/a/mkdocs.yml new file mode 100644 index 0000000..89b78e5 --- /dev/null +++ b/tests/fixtures/monorepo/components/a/mkdocs.yml @@ -0,0 +1,4 @@ +site_name: Component A + +nav: + - Home: "README.md" diff --git a/tests/fixtures/monorepo/components/b/docs/README.md b/tests/fixtures/monorepo/components/b/docs/README.md new file mode 100644 index 0000000..54480bb --- /dev/null +++ b/tests/fixtures/monorepo/components/b/docs/README.md @@ -0,0 +1 @@ +# Docs for B diff --git a/tests/fixtures/monorepo/components/b/mkdocs.yml b/tests/fixtures/monorepo/components/b/mkdocs.yml new file mode 100644 index 0000000..2439516 --- /dev/null +++ b/tests/fixtures/monorepo/components/b/mkdocs.yml @@ -0,0 +1,4 @@ +site_name: Component B + +nav: + - Home: "README.md" diff --git a/tests/fixtures/monorepo/components/c/docs/README.md b/tests/fixtures/monorepo/components/c/docs/README.md new file mode 100644 index 0000000..0ce3a58 --- /dev/null +++ b/tests/fixtures/monorepo/components/c/docs/README.md @@ -0,0 +1 @@ +# Docs for C diff --git a/tests/fixtures/monorepo/components/c/mkdocs.yml b/tests/fixtures/monorepo/components/c/mkdocs.yml new file mode 100644 index 0000000..ffbc6c1 --- /dev/null +++ b/tests/fixtures/monorepo/components/c/mkdocs.yml @@ -0,0 +1,4 @@ +site_name: Component C + +nav: + - Home: "README.md" diff --git a/tests/fixtures/monorepo/components/d/README.md b/tests/fixtures/monorepo/components/d/README.md new file mode 100644 index 0000000..a045e60 --- /dev/null +++ b/tests/fixtures/monorepo/components/d/README.md @@ -0,0 +1,3 @@ +# Component D + +This will not be included diff --git a/tests/fixtures/monorepo/components/d/e/docs/README.md b/tests/fixtures/monorepo/components/d/e/docs/README.md new file mode 100644 index 0000000..c83a82f --- /dev/null +++ b/tests/fixtures/monorepo/components/d/e/docs/README.md @@ -0,0 +1 @@ +# Docs for E diff --git a/tests/fixtures/monorepo/components/d/e/mkdocs.yml b/tests/fixtures/monorepo/components/d/e/mkdocs.yml new file mode 100644 index 0000000..8ec4728 --- /dev/null +++ b/tests/fixtures/monorepo/components/d/e/mkdocs.yml @@ -0,0 +1,4 @@ +site_name: Component E + +nav: + - Home: "README.md" diff --git a/tests/fixtures/monorepo/components/randomfile b/tests/fixtures/monorepo/components/randomfile new file mode 100644 index 0000000..25e04ba --- /dev/null +++ b/tests/fixtures/monorepo/components/randomfile @@ -0,0 +1 @@ +File present to test that it is successfully ignored by wildcard include. diff --git a/tests/fixtures/monorepo/docs/authentication.md b/tests/fixtures/monorepo/docs/authentication.md new file mode 100644 index 0000000..80fbc08 --- /dev/null +++ b/tests/fixtures/monorepo/docs/authentication.md @@ -0,0 +1,10 @@ +# Authentication + +This explains how to authenticate. + +## Authorization Header + +To authenticate, simply pass in your access token in the form of a `Authorization: Bearer {access_token}` header in your HTTP request to our API. + +- Markdown source: `sample-docs/docs/authentication.md` +- Permalink: diff --git a/tests/fixtures/monorepo/docs/index.md b/tests/fixtures/monorepo/docs/index.md new file mode 100644 index 0000000..ce27a1e --- /dev/null +++ b/tests/fixtures/monorepo/docs/index.md @@ -0,0 +1,12 @@ +# Welcome to our Developer Platform + +This contains the documentation for our Developer Platform. + +## API + +- [v1 API Reference](./versions/v1/reference.md) +- [v2 API Reference](./versions/v2/reference.md) +- [v3 API Reference](./versions/v3/reference.md) + +- Markdown source: `sample-docs/docs/index.md` +- Permalink: diff --git a/tests/fixtures/monorepo/mkdocs.yml b/tests/fixtures/monorepo/mkdocs.yml new file mode 100644 index 0000000..3b9798e --- /dev/null +++ b/tests/fixtures/monorepo/mkdocs.yml @@ -0,0 +1,18 @@ +site_name: Cats API + +nav: + - Intro: 'index.md' + - Authentication: 'authentication.md' + - Components: '*include ./components/*/mkdocs.yml' + - API: + - v1: '!include ./v1/mkdocs.yml' + - v2: '!include ./v2/mkdocs.yml' + - v3: '!include ./v3/mkdocs.yml' + +theme: + name: material + +plugins: + - search + - git-revision-date-localized + - monorepo diff --git a/tests/fixtures/monorepo/v1/docs/changelog.md b/tests/fixtures/monorepo/v1/docs/changelog.md new file mode 100644 index 0000000..84b564d --- /dev/null +++ b/tests/fixtures/monorepo/v1/docs/changelog.md @@ -0,0 +1,6 @@ +# Changelog + +This contains the changelog for the v1 API. + +- Markdown source: `sample-docs/v1/docs/changelog.md` +- Permalink: diff --git a/tests/fixtures/monorepo/v1/docs/reference.md b/tests/fixtures/monorepo/v1/docs/reference.md new file mode 100644 index 0000000..4b1fce3 --- /dev/null +++ b/tests/fixtures/monorepo/v1/docs/reference.md @@ -0,0 +1,18 @@ +# v1 API reference + +This contains the v1 API reference. + +## GET /v1/cats + +Returns a list of cats. + +## GET /v1/search + +Parameters: + +- `q` = A string containing the search query for a cat name. + +Returns a list of cats that match the search query. + +- Markdown source: `sample-docs/v1/docs/reference.md` +- Permalink: diff --git a/tests/fixtures/monorepo/v1/mkdocs.yml b/tests/fixtures/monorepo/v1/mkdocs.yml new file mode 100644 index 0000000..ede30e7 --- /dev/null +++ b/tests/fixtures/monorepo/v1/mkdocs.yml @@ -0,0 +1,5 @@ +site_name: versions/v1 + +nav: + - Reference: "reference.md" + - Changelog: "changelog.md" diff --git a/tests/fixtures/monorepo/v2/docs/changelog.md b/tests/fixtures/monorepo/v2/docs/changelog.md new file mode 100644 index 0000000..8b6bdee --- /dev/null +++ b/tests/fixtures/monorepo/v2/docs/changelog.md @@ -0,0 +1,6 @@ +# Changelog + +This contains the changelog for the v2 API. + +- Markdown source: `sample-docs/v2/docs/changelog.md` +- Permalink: diff --git a/tests/fixtures/monorepo/v2/docs/migrating.md b/tests/fixtures/monorepo/v2/docs/migrating.md new file mode 100644 index 0000000..c9f92ef --- /dev/null +++ b/tests/fixtures/monorepo/v2/docs/migrating.md @@ -0,0 +1,6 @@ +# Migration Guide to v2 + +This contains the migration guide to the v2 API from the v1 API. + +- Markdown source: `sample-docs/v2/docs/migrating.md` +- Permalink: diff --git a/tests/fixtures/monorepo/v2/docs/reference.md b/tests/fixtures/monorepo/v2/docs/reference.md new file mode 100644 index 0000000..b8866fd --- /dev/null +++ b/tests/fixtures/monorepo/v2/docs/reference.md @@ -0,0 +1,26 @@ +# v2 API reference + +This contains the v2 API reference. + +## GET /v2/cats + +Returns a list of all cats. + +## GET /v2/breeds + +Returns a list of all breeds of cats. + +## GET /v2/breeds/:breed_name/cats + +Returns a list of all cats under the `:breed_name` breed. + +## GET /v2/search + +Parameters: + +- `q` = A string containing the search query for a cat name or breed. + +Returns a list of cats that match the search query. + +- Markdown source: `sample-docs/v2/docs/reference.md` +- Permalink: diff --git a/tests/fixtures/monorepo/v2/mkdocs.yml b/tests/fixtures/monorepo/v2/mkdocs.yml new file mode 100644 index 0000000..be8ae95 --- /dev/null +++ b/tests/fixtures/monorepo/v2/mkdocs.yml @@ -0,0 +1,6 @@ +site_name: versions/v2 + +nav: + - Migrating to v2: "migrating.md" + - Reference: "reference.md" + - Changelog: "changelog.md" diff --git a/tests/fixtures/monorepo/v3/docs/changelog.md b/tests/fixtures/monorepo/v3/docs/changelog.md new file mode 100644 index 0000000..657408e --- /dev/null +++ b/tests/fixtures/monorepo/v3/docs/changelog.md @@ -0,0 +1,6 @@ +# Changelog + +This contains the changelog for the v3 API. + +- Markdown source: `sample-docs/v3/docs/changelog.md` +- Permalink: diff --git a/tests/fixtures/monorepo/v3/docs/migrating.md b/tests/fixtures/monorepo/v3/docs/migrating.md new file mode 100644 index 0000000..26a856f --- /dev/null +++ b/tests/fixtures/monorepo/v3/docs/migrating.md @@ -0,0 +1,6 @@ +# Migration Guide to v3 + +This contains the migration guide to the v3 API from the v2 API. + +- Markdown source: `sample-docs/v3/docs/migrating.md` +- Permalink: diff --git a/tests/fixtures/monorepo/v3/docs/reference.md b/tests/fixtures/monorepo/v3/docs/reference.md new file mode 100644 index 0000000..156c1b2 --- /dev/null +++ b/tests/fixtures/monorepo/v3/docs/reference.md @@ -0,0 +1,30 @@ +# v3 API reference + +This contains the v3 API reference. + +## GET /v3/cats + +Returns a list of all cats. + +## GET /v3/breeds + +Returns a list of all breeds of cats. + +## GET /v3/breeds/:breed_name/cats + +Returns a list of all cats under the `:breed_name` breed. + +## GET /v3/search + +Parameters: + +- `q` = A string containing the search query for a cat name or breed. + +Returns a list of cats that match the search query. + +- Markdown source: `sample-docs/v3/docs/reference.md` +- Permalink: + +## GET /v3/cat/:name/lives_left + +Returns how many lives the cat named `:name` still has left. diff --git a/tests/fixtures/monorepo/v3/mkdocs.yml b/tests/fixtures/monorepo/v3/mkdocs.yml new file mode 100644 index 0000000..8082133 --- /dev/null +++ b/tests/fixtures/monorepo/v3/mkdocs.yml @@ -0,0 +1,3 @@ +site_name: versions/v3 + +docs_dir: ./docs diff --git a/tests/test_builds.py b/tests/test_builds.py index f873aa3..ab10cc6 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -127,6 +127,10 @@ def setup_clean_mkdocs_folder(mkdocs_yml_path, output_path): if "gen-files" in mkdocs_yml_path: shutil.copyfile(str(Path(mkdocs_yml_path).parent / "gen_pages.py"), str(testproject_path / "gen_pages.py")) + # Copy monorepo files + if "monorepo" in mkdocs_yml_path: + shutil.copytree("tests/fixtures/monorepo", str(testproject_path), dirs_exist_ok=True) + return testproject_path @@ -313,27 +317,27 @@ def validate_mkdocs_file(temp_path: str, mkdocs_yml_file: str): MKDOCS_FILES = [ - 'basic_project/mkdocs_theme_no_locale.yml', - 'basic_project/mkdocs.yml', - 'basic_project/mkdocs_theme_timeago_locale.yml', + 'basic_project/mkdocs_creation_date.yml', + 'basic_project/mkdocs_custom_type.yml', 'basic_project/mkdocs_datetime.yml', + 'basic_project/mkdocs_exclude.yml', + 'basic_project/mkdocs_fallback_to_build_date.yml', + 'basic_project/mkdocs_locale.yml', + 'basic_project/mkdocs_meta.yml', 'basic_project/mkdocs_plugin_locale.yml', - 'basic_project/mkdocs_with_override.yml', + 'basic_project/mkdocs.yml', + 'basic_project/mkdocs_theme_timeago_locale.yml', 'basic_project/mkdocs_theme_language.yml', 'basic_project/mkdocs_theme_locale_and_language.yml', 'basic_project/mkdocs_theme_locale_disabled.yml', 'basic_project/mkdocs_theme_timeago.yml', 'basic_project/mkdocs_theme_locale.yml', + 'basic_project/mkdocs_theme_no_locale.yml', 'basic_project/mkdocs_theme_timeago_override.yml', 'basic_project/mkdocs_theme_timeago_instant.yml', - 'basic_project/mkdocs_creation_date.yml', 'basic_project/mkdocs_timeago_locale.yml', 'basic_project/mkdocs_timeago.yml', - 'basic_project/mkdocs_fallback_to_build_date.yml', - 'basic_project/mkdocs_locale.yml', - 'basic_project/mkdocs_exclude.yml', - 'basic_project/mkdocs_meta.yml', - 'basic_project/mkdocs_custom_type.yml', + 'basic_project/mkdocs_with_override.yml', # 'i18n/mkdocs.yml' ] @@ -704,3 +708,19 @@ def test_ignored_commits(tmp_path): page_with_tag = testproject_path / "site/page_with_tag/index.html" contents = page_with_tag.read_text(encoding="utf8") assert "May 4, 2018" in contents + + + +def test_monorepo_compat(tmp_path): + testproject_path = setup_clean_mkdocs_folder( + "tests/fixtures/monorepo/mkdocs.yml", tmp_path + ) + repo = setup_commit_history(testproject_path) + result = build_docs_setup(testproject_path) + + # author = "Test Person " + # with working_directory(testproject_path): + # repo.git.add(".") + # repo.git.commit(message="add all", author=author, date="1500854705") + + assert result.exit_code == 0, f"'mkdocs build' command failed with:\n\n{result.stdout}" diff --git a/uv.lock b/uv.lock index f62107f..a4ce7b3 100644 --- a/uv.lock +++ b/uv.lock @@ -691,6 +691,7 @@ dev = [ { name = "mkdocs-gen-files" }, { name = "mkdocs-git-authors-plugin" }, { name = "mkdocs-material" }, + { name = "mkdocs-monorepo-plugin" }, { name = "mkdocs-static-i18n" }, { name = "pytest" }, { name = "pytest-cov", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, @@ -713,6 +714,7 @@ dev = [ { name = "mkdocs-gen-files", specifier = ">=0.5.0" }, { name = "mkdocs-git-authors-plugin", specifier = ">=0.9.2" }, { name = "mkdocs-material", specifier = ">=9.6.7" }, + { name = "mkdocs-monorepo-plugin", specifier = ">=1.1.0" }, { name = "mkdocs-static-i18n", specifier = ">=1.3.0" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "pytest-cov", specifier = ">=5.0.0" }, @@ -751,6 +753,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, ] +[[package]] +name = "mkdocs-monorepo-plugin" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, + { name = "python-slugify" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/6c/5b2a34fd63fe20724e2edf1879e977b40453efe40e1c385a05f38b420664/mkdocs-monorepo-plugin-1.1.0.tar.gz", hash = "sha256:ccc566e166aac5ae7fade498c15c4a337a4892d238629b51aba8ef3fc7099034", size = 13435 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/33/4cc6c70223aee511244f8fe7706df70d1cd253d1446ab466c73f9dfbaab5/mkdocs_monorepo_plugin-1.1.0-py3-none-any.whl", hash = "sha256:7bbfd9756a7fdecf64d6105dad96cce7e7bb5f0d6cfc2bfda31a1919c77cc3b9", size = 14312 }, +] + [[package]] name = "mkdocs-static-i18n" version = "1.3.0" @@ -891,6 +906,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] +[[package]] +name = "python-slugify" +version = "8.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "text-unidecode" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051 }, +] + [[package]] name = "pytz" version = "2025.1" @@ -1031,6 +1058,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, ] +[[package]] +name = "text-unidecode" +version = "1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154 }, +] + [[package]] name = "tomli" version = "2.2.1" From 21bc7ca550a341743777db53c81086862a3dcfb5 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 10:20:41 +0000 Subject: [PATCH 27/44] better error for monorepo --- tests/test_builds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_builds.py b/tests/test_builds.py index ab10cc6..ab6960f 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -723,4 +723,4 @@ def test_monorepo_compat(tmp_path): # repo.git.add(".") # repo.git.commit(message="add all", author=author, date="1500854705") - assert result.exit_code == 0, f"'mkdocs build' command failed with:\n\n{result.stdout}" + assert result.exit_code == 0, f"'mkdocs build' command failed with:\n\n{result.stdout}\n\n{result.stderr}" From 07f0cb79335c4e3c6fe09c91dedf3f7bf69061a6 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 10:29:32 +0000 Subject: [PATCH 28/44] apply ruff format --- .../dates.py | 70 ++--- .../plugin.py | 22 +- tests/fixtures/mkdocs-gen-files/gen_pages.py | 1 - .../fixtures/with_mknotebooks/docs/demo.ipynb | 91 ++++--- tests/test_builds.py | 257 ++++++++---------- tests/test_dates.py | 32 +-- tests/test_exclude.py | 4 +- tests/test_parse_git_ignore_revs.py | 3 +- 8 files changed, 223 insertions(+), 257 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/dates.py b/src/mkdocs_git_revision_date_localized_plugin/dates.py index 3fd4dd6..c66e4a5 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/dates.py +++ b/src/mkdocs_git_revision_date_localized_plugin/dates.py @@ -43,49 +43,49 @@ def get_date_formats( def strftime_to_babel_format(fmt: str) -> str: """ Convert strftime format string to Babel format pattern. - + Args: fmt (str): strftime format string - + Returns: str: Babel format pattern """ # Dictionary mapping strftime directives to Babel format patterns mapping = { - '%a': 'EEE', # Weekday abbreviated - '%A': 'EEEE', # Weekday full - '%b': 'MMM', # Month abbreviated - '%B': 'MMMM', # Month full - '%c': '', # Locale's date and time (not directly mappable) - '%d': 'dd', # Day of month zero-padded - '%-d': 'd', # Day of month - '%e': 'd', # Day of month space-padded - '%f': 'SSSSSS', # Microsecond - '%H': 'HH', # Hour 24h zero-padded - '%-H': 'H', # Hour 24h - '%I': 'hh', # Hour 12h zero-padded - '%-I': 'h', # Hour 12h - '%j': 'DDD', # Day of year - '%m': 'MM', # Month zero-padded - '%-m': 'M', # Month - '%M': 'mm', # Minute zero-padded - '%-M': 'm', # Minute - '%p': 'a', # AM/PM - '%S': 'ss', # Second zero-padded - '%-S': 's', # Second - '%w': 'e', # Weekday as number - '%W': 'w', # Week of year - '%x': '', # Locale's date (not directly mappable) - '%X': '', # Locale's time (not directly mappable) - '%y': 'yy', # Year without century - '%Y': 'yyyy', # Year with century - '%z': 'Z', # UTC offset - '%Z': 'z', # Timezone name - '%%': '%' # Literal % + "%a": "EEE", # Weekday abbreviated + "%A": "EEEE", # Weekday full + "%b": "MMM", # Month abbreviated + "%B": "MMMM", # Month full + "%c": "", # Locale's date and time (not directly mappable) + "%d": "dd", # Day of month zero-padded + "%-d": "d", # Day of month + "%e": "d", # Day of month space-padded + "%f": "SSSSSS", # Microsecond + "%H": "HH", # Hour 24h zero-padded + "%-H": "H", # Hour 24h + "%I": "hh", # Hour 12h zero-padded + "%-I": "h", # Hour 12h + "%j": "DDD", # Day of year + "%m": "MM", # Month zero-padded + "%-m": "M", # Month + "%M": "mm", # Minute zero-padded + "%-M": "m", # Minute + "%p": "a", # AM/PM + "%S": "ss", # Second zero-padded + "%-S": "s", # Second + "%w": "e", # Weekday as number + "%W": "w", # Week of year + "%x": "", # Locale's date (not directly mappable) + "%X": "", # Locale's time (not directly mappable) + "%y": "yy", # Year without century + "%Y": "yyyy", # Year with century + "%z": "Z", # UTC offset + "%Z": "z", # Timezone name + "%%": "%", # Literal % } - + result = fmt for strftime_code, babel_code in mapping.items(): result = result.replace(strftime_code, babel_code) - - return result \ No newline at end of file + + return result diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 126bee2..6c90a44 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -144,7 +144,6 @@ 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): pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count())) results = [] @@ -154,9 +153,7 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di # Support plugins like monorep 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] - result = pool.apply_async( - self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit) - ) + result = pool.apply_async(self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit)) results.append((abs_src_path, result)) pool.close() pool.join() @@ -173,10 +170,10 @@ def on_files(self, files: Files, config: MkDocsConfig): """ if not self.config.get("enabled") or not self.config.get("enable_parallel_processing"): return - + # Support monorepo/techdocs, which copies the docs_dir to a temporary directory - if "monorepo" in config.get('plugins', {}): - original_source = config.get('plugins').get('monorepo').merger.files_source_dir + if "monorepo" in config.get("plugins", {}): + original_source = config.get("plugins").get("monorepo").merger.files_source_dir else: original_source = None @@ -185,7 +182,6 @@ def on_files(self, files: Files, config: MkDocsConfig): if not self.created_commits: self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=True) - def on_page_markdown(self, markdown: str, page: Page, config: config_options.Config, files, **kwargs) -> str: """ Replace jinja2 tags in markdown and templates with the localized dates. @@ -240,7 +236,9 @@ 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(page.file.abs_src_path, (None, None)) + last_revision_hash, last_revision_timestamp = self.last_revision_commits.get( + page.file.abs_src_path, (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, @@ -314,8 +312,10 @@ 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(page.file.abs_src_path, (None, None)) - if first_revision_timestamp is None: + first_revision_hash, first_revision_timestamp = self.created_commits.get( + page.file.abs_src_path, (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, diff --git a/tests/fixtures/mkdocs-gen-files/gen_pages.py b/tests/fixtures/mkdocs-gen-files/gen_pages.py index 059b0d4..0825cfb 100644 --- a/tests/fixtures/mkdocs-gen-files/gen_pages.py +++ b/tests/fixtures/mkdocs-gen-files/gen_pages.py @@ -2,4 +2,3 @@ with mkdocs_gen_files.open("foo.md", "w") as f: print("Hello, world!", file=f) - diff --git a/tests/fixtures/with_mknotebooks/docs/demo.ipynb b/tests/fixtures/with_mknotebooks/docs/demo.ipynb index 05bcb32..66ee88a 100644 --- a/tests/fixtures/with_mknotebooks/docs/demo.ipynb +++ b/tests/fixtures/with_mknotebooks/docs/demo.ipynb @@ -1,30 +1,4 @@ { - "metadata": { - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3-final" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "Python 3.8.3 64-bit ('base': conda)", - "display_name": "Python 3.8.3 64-bit ('base': conda)", - "metadata": { - "interpreter": { - "hash": "61a9efb557ee20b19fda2d58cb63f9a3bf86c2530fcd43d63aa6e0adea42a5e4" - } - } - } - }, - "nbformat": 4, - "nbformat_minor": 2, "cells": [ { "cell_type": "code", @@ -35,6 +9,7 @@ "import pandas as pd\n", "import numpy as np\n", "import datetime\n", + "\n", "%matplotlib inline" ] }, @@ -44,8 +19,8 @@ "metadata": {}, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "This notebook last ran at 2020-10-26 15:45:16.815445\n" ] @@ -56,11 +31,11 @@ ] }, { + "cell_type": "markdown", + "metadata": {}, "source": [ "## A df" - ], - "cell_type": "markdown", - "metadata": {} + ] }, { "cell_type": "code", @@ -68,8 +43,8 @@ "metadata": {}, "outputs": [ { - "output_type": "execute_result", "data": { + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
timeamplitude
00.00.000000
10.10.099833
20.20.198669
30.30.295520
40.40.389418
\n
", "text/plain": [ " time amplitude\n", "0 0.0 0.000000\n", @@ -77,39 +52,65 @@ "2 0.2 0.198669\n", "3 0.3 0.295520\n", "4 0.4 0.389418" - ], - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
timeamplitude
00.00.000000
10.10.099833
20.20.198669
30.30.295520
40.40.389418
\n
" + ] }, + "execution_count": 4, "metadata": {}, - "execution_count": 4 + "output_type": "execute_result" } ], "source": [ - "df = pd.DataFrame({\"time\":np.arange(0, 10, 0.1)})\n", - "df[\"amplitude\"] = np.sin(df.time)\n", + "df = pd.DataFrame({\"time\": np.arange(0, 10, 0.1)})\n", + "df[\"amplitude\"] = np.sin(df.time)\n", "df.head(5)" ] }, { - "source": [ - "ax = df.plot()" - ], "cell_type": "code", - "metadata": {}, "execution_count": 5, + "metadata": {}, "outputs": [ { - "output_type": "display_data", "data": { - "text/plain": "
", + "image/png": "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\n", "image/svg+xml": "\n\n\n\n \n \n \n \n 2020-10-26T15:45:51.963649\n image/svg+xml\n \n \n Matplotlib v3.3.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXAAAAD4CAYAAAD1jb0+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAAAuKklEQVR4nO3dd3hUVf7H8fdJTyAkJHRCSCABkgChhLIgUlU6KvaKq8tv3V3bqiu7FgIqi72xdhB1dcW2GorSUREpoacnQAIJAVJISC8z5/fHjS4iKCST3Cnf1/PkITOZmfu93MknZ84951yltUYIIYTjcTO7ACGEEI0jAS6EEA5KAlwIIRyUBLgQQjgoCXAhhHBQHi25sXbt2umwsLCW3KQQQji8nTt3Fmqt2595f4sGeFhYGImJiS25SSGEcHhKqZyz3S9dKEII4aAkwIUQwkFJgAshhIP6zT5wpdQSYCpwQmvdt+G+IGAZEAZkA9dorU82poC6ujpyc3Oprq5uzNPFWfj4+BASEoKnp6fZpQghmtH5nMRcCiwC3jvtvjnAeq31QqXUnIbbDzWmgNzcXPz9/QkLC0Mp1ZiXEKfRWlNUVERubi7h4eFmlyOEaEa/2YWitf4WKD7j7hnAuw3fvwtc3tgCqqurCQ4OlvC2EaUUwcHB8olGCBfQ2D7wjlrr/IbvjwEdz/VApdRspVSiUiqxoKDgXI9pZBnibOT/UwjX0OSTmNpYj/aca9Jqrd/UWsdprePat//FOHQhhHBqh4sqmbc8mXqL1eav3dgAP66U6gzQ8O8J25XUskpKSnj11VcBOHr0KFdddZXJFQkhnEFVrYXn16Qz4YVvWLbjCKn5ZTbfRmMDPAG4teH7W4EvbVNOyzs9wLt06cKnn35qckVCCEemtWbV/nwmPP8NL2/IYmJMJzbcP4Z+IQE239b5DCP8DzAGaKeUygXmAguBj5VStwM5wDU2r6yFzJkzhwMHDjBgwAAiIyNJTU0lKSmJpUuX8sUXX1BRUUFmZiYPPPAAtbW1vP/++3h7e7Nq1SqCgoI4cOAAf/7znykoKMDPz4+33nqLPn36mL1bQggTZB4vI355Mt9nFdGnkz/LZg9nWI/gZtvebwa41vr6c/xovI1rYd7yZFKOnrLpa0Z3acPcaTHn/PnChQtJSkpiz549ZGdnM3Xq1J9+lpSUxO7du6muriYiIoKnnnqK3bt3c9999/Hee+9x7733Mnv2bF5//XUiIyPZtm0bf/rTn9iwYYNN90EIYd9OVdfx0rpM3t2STStvD+bPiOGGoaF4uDfvXMkWXczK0YwdOxZ/f3/8/f0JCAhg2rRpAPTr1499+/ZRXl7Oli1buPrqq396Tk1NjVnlCiFamNWq+XRXLk9/nUZRRS3XDQnlwct6E9TKq0W2b1cB/mstZTN4e3v/9L2bm9tPt93c3Kivr8dqtRIYGMiePXtMqlAIYZa9R0qYm5DMniMlDAoN5J1ZQ5uln/vXuPxaKP7+/pSVNe7scJs2bQgPD+eTTz4BjJMXe/futWV5Qgg7U1Rew0Of7uPyV78n92QVz14dy6d/HNHi4Q121gI3Q3BwMCNHjqRv375ERUVd8PM/+OAD7rzzTp544gnq6uq47rrriI2NbYZKhRBmqrdYeX9rDs+vzaCq1sIdF4Vz9/hI/H3MW3NIGfNwWkZcXJw+84IOqampjQpO8evk/1UI2/nhQBHzlieTdqyMiyLaET89mogO/i22faXUTq113Jn3u3wLXAghzuVoSRVPrkpl5b58ugb68vpNg7kspqPdLFchAS6EEGeorrOwePMhFm3Iwqo1906I5I+je+Lj6W52aT8jAS6EEKdZn3qc+StSyCmqZGJMJx6eEkW3ID+zyzorCXAhhAAOFVYwf3kyG9ML6Nm+Fe/fPpRRkfa9AJ8EuBDCpVXU1PPKhiwWbz6It4c7D0+OYtbIMDybeRalLUiACyFcktaahL1HWbAqleOnapg5KISHJvWmg7+P2aWdN/v/E+OgWrduDfx8ido9e/awatWqC36t+Ph4nn32WZvWJ4QrSzl6imvf2Mo9H+2hg78Pn905gueuiXWo8AZpgTe705eo3bNnD4mJiUyePNnkqoRwTSWVtTy3JoMPtuUQ6OfFP6/sxzVx3XB3s49hgRdKWuDA5ZdfzuDBg4mJieHNN98EjBb0gw8+SExMDBMmTGD79u2MGTOGHj16kJCQAMDSpUuZMWMGY8aMITIyknnz5v3itbOzs+nbty+1tbU89thjLFu2jAEDBrBs2bJftKz79u1LdnY2AE8++SS9evXioosuIj09/afHHDhwgIkTJzJ48GBGjRpFWlpaM/7PCOEcLFbNh9sOM/bZTXywLYebh3dn4/1juH5oqMOGN9hbC/yrOXBsv21fs1M/mLTwVx+yZMkSgoKCqKqqYsiQIcycOZOKigrGjRvHM888wxVXXMEjjzzC2rVrSUlJ4dZbb2X69OkAbN++naSkJPz8/BgyZAhTpkwhLu4XE6bw8vJi/vz5JCYmsmjRIsDoGjmbnTt38tFHH7Fnzx7q6+sZNGgQgwcPBpDla4W4QDtzTjI3IYmkvFMMDQ9i3vQYojq3Mbssm7CvADfJyy+/zH//+18Ajhw5QmZmJl5eXkycOBEwlo/19vbG09OTfv36/dRKBrjkkksIDjYWbL/yyivZvHnzWQP8Qnz33XdcccUV+PkZY09//GMhy9cKcf5OlFWz8Ks0Pt+VR6c2Prx8/UCm9e9sN7MobcG+Avw3WsrNYdOmTaxbt44ffvgBPz8/xowZQ3V1NZ6enj8d6LMtJfujM98MF/Lm8PDwwGr934VOq6urf/XxsnytEL+ttt7Ku1uyeWl9JjX1Fv40pid/HhtBK2/7ijtbcPk+8NLSUtq2bYufnx9paWls3br1gp6/du1aiouLqaqq4osvvmDkyJHnfOyZS9eGhYWxa9cuAHbt2sWhQ4cAuPjii/niiy+oqqqirKyM5cuXA7J8rRC/5duMAia99C1PrkplaHgQa+4bzd8m9nHK8AYJcCZOnEh9fT1RUVHMmTOH4cOHX9Dzhw4dysyZM+nfvz8zZ8781e6TsWPHkpKS8tNJzJkzZ1JcXExMTAyLFi2iV69eAAwaNIhrr72W2NhYJk2axJAhQ356jQ8++IDFixcTGxtLTEwMX37psNeTFsJmjhRXMvu9RG5Zsp16q2bxrXEsmTWE8HatzC6tWclysk2wdOnSn52UtCeO/P8qxPmqqrXw2jcHeOObA7gpxV/GRXDHqHC8Pexr0ammkuVkhRBOQ2vN6uRjPL4ilbySKqbFduEfk/vQOcDX7NJalAR4E8yaNYtZs2aZXYYQLiXrRBnxCSlsziqkTyd/Ppo9nOE9gs0uyxR2EeBaa6ca2mO2luwWE6KllFXX8dK6TJZuycbPy51502O4cVgoHg6w6FRzMT3AfXx8KCoqIjg4WELcBrTWFBUV4ePjWGs6CHEuVqvm8915LPwqjaKKGq6N68aDl/UmuLW32aWZzvQADwkJITc3l4KCArNLcRo+Pj6EhISYXYYQTbY/t5THEpLYfbiEAd0CWTIrjv4hgWaXZTdMD3BPT0/Cw8PNLkMIYUeKymt4dk06H+04QnArb569OpYrB3bFzYHXLWkOpge4EEL8qN5i5YNth3luTTqVtRZuHxnO3RMiaePjaXZpdkkCXAhhF7YeLCI+IZm0Y2VcFNGO+OnRRHTwN7ssu9akAFdK3QfcAWhgP3Cb1vrXF/QQQojT5JdWsWBVGsv3HqVroC+v3zSIy2I6yaCG89DoAFdKdQXuBqK11lVKqY+B64ClNqpNCOHEauotvP3dIRZtyMKqNfeMj+SPo3vi6+VcsyibU1O7UDwAX6VUHeAHHG16SUIIZ7ch7Tjzl6eQXVTJZTEdeWRKNN2C/Mwuy+E0OsC11nlKqWeBw0AVsEZrvebMxymlZgOzAUJDQxu7OSGEE8gurGD+ihQ2pJ2gR/tWvPf7oVzcq73ZZTmspnShtAVmAOFACfCJUuomrfW/T3+c1vpN4E0wFrNqfKlCCEdVWVvPog1ZvP3dITzdFf+Y3IdZI8Lx8nDdWZS20JQulAnAIa11AYBS6nNgBPDvX32WEMJlaK1ZsS+fBatSyS+t5sqBXZkzqQ8d2shMYVtoSoAfBoYrpfwwulDGA4m//hQhhKtIzT9FfEIy2w4VE9OlDYtuGMjg7kFml+VUmtIHvk0p9SmwC6gHdtPQVSKEcF2llXU8vzad97fmEODryZNX9OW6IY599Xd71aRRKFrrucBcG9UihHBgFqvm48QjPLM6nZLKWm4YFsoDl/Ym0M/L7NKclszEFEI02a7DJ5n7ZTL780oZEtaW+OlDiekSYHZZTk8CXAjRaCfKqnnqq3Q+25VLxzbevHTdAKbHdpFZlC1EAlwIccHqLFbe3ZLNS+syqa638MfRPblrXITTXv3dXsn/thDignyfVcjchGSyTpQzpnd7HpsaTY/2rc0uyyVJgAshzkvuyUqeWJHK18nHCA3y4+1b4hgf1UG6S0wkAS6E+FXVdRbe+OYgr27Kwk0pHri0F3eM6oGPpyw6ZTYJcCHEWWmtWZ18nCdWppB7soop/Tvz8OQougT6ml2aaCABLoT4hawT5cxbnsx3mYX07ujPh38Yxoie7cwuS5xBAlwI8ZOy6jpeXp/JO99n4+vlzmNTo7n5d93xdJdFp+yRBLgQAqtV89/deSz8Oo3C8hquGdyNByf2pl1rb7NLE79CAlwIF5eUV8rchGR25pwktlsgb98SR2y3QLPLEudBAlwIF1VcUcszq9P5aMdhglt58fTM/lw1OAQ3WXTKYUiAC+Fi6i1WPtx+mOfWZFBeU89tI8K5Z0IkAb6eZpcmLpAEuBAuZNvBIuYmJJN2rIwRPYOJnx5Dr47+ZpclGkkCXAgXcKy0mgWrUknYe5Sugb68euMgJvXtJLMoHZwEuBBOrKbewuLNh1i0IYt6q+bucRHcOSYCXy+ZRekMJMCFcFIb004wf0UKhworuCS6I49OiSY02M/ssoQNSYAL4WSyCyt4fEUK69NO0KNdK979/VBG92pvdlmiGUiAC+EkKmvreXXjAd789iCe7op/TO7DrBHheHnILEpnJQEuhIPTWrNiXz4LVqWSX1rNFQO78vdJfejQxsfs0kQzkwAXwoGlHTtFfEIyWw8WE925Da9cP5C4sCCzyxItRAJcCAdUWlnHC+syeH9rDv4+HjxxeV+uHxqKu8yidCkS4EI4EKtV83HiEZ5enU5JZS03DAvl/kt607aVl9mlCRNIgAvhIHYfPsnchGT25ZYyJKwtc6cNpW/XALPLEiaSABfCzhWU1fDU12l8ujOXDv7evHjtAGYM6CKzKIUEuBD2qs5i5b0fcnhxbQbV9Rb+b3QP7hoXSWtv+bUVBnknCGGHtmQVMjchmcwT5Yzu1Z7HpkXTs31rs8sSdqZJAa6UCgTeBvoCGvi91voHG9QlhEvKPVnJglWprNp/jNAgP96+JY7xUR2ku0ScVVNb4C8BX2utr1JKeQGy0IIQjVBdZ+GNbw7y2jdZANx/SS/+cHEPfDxl0Slxbo0OcKVUAHAxMAtAa10L1NqmLCFcg9aaNSnHeWJlCkeKq5jSrzP/mBJF10Bfs0sTDqApLfBwoAB4RykVC+wE7tFaV5z+IKXUbGA2QGhoaBM2J4RzyTpRzrzlyXyXWUivjq358I5hjIhoZ3ZZwoEorXXjnqhUHLAVGKm13qaUegk4pbV+9FzPiYuL04mJiY2rVAgnUVZdxysbsliy+RC+Xu7cN6EXN/+uO57usuiUODul1E6tddyZ9zelBZ4L5GqttzXc/hSY04TXE8Kpaa35Yk8eC1alUVBWwzVxIfxtYh/atfY2uzThoBod4FrrY0qpI0qp3lrrdGA8kGK70oRwHkl5pcQnJJOYc5LYkADeuiWOAd0CzS5LOLimjkK5C/igYQTKQeC2ppckhPM4WVHLs2vS+XD7YYL8vHh6Zn+uGhyCmyw6JWygSQGutd4D/KJfRghXZ7FqPtx+mOfWpFNWXc+sEWHcO6EXAb6eZpcmnIjMxBTCxnZkFzP3y2RS8k8xvEcQ86b3pXcnf7PLEk5IAlwIGzl+qpp/rkrliz1H6Rzgw6IbBjKlX2eZRSmajQS4EE1UW29lyfeHeGV9JnVWzV3jIrhzTE/8vOTXSzQveYcJ0QSb0k8wf3kKBwsrmBDVkcemRhMaLCtKiJYhAS5EIxwuqmT+ihTWpR4nvF0rlt42hDG9O5hdlnAxEuBCXICqWguvbsrijW8P4uGmeGhiH35/URjeHrLolGh5EuBCnAetNav2H+PJlSkcLa1mxoAu/H1SFJ0CfMwuTbgwCXAhfkPG8TLmfpnMDweLiOrchhevG8jQ8CCzyxJCAlyIcymtquPFdRm890MOrb09eHxGDDcM6467zKIUdkICXIgzWK2aT3fl8tRXaRRX1nL90FAeuLQ3Qa28zC5NiJ+RABfiNHuOlDA3IZm9R0oY3L0t704fSt+uAWaXJcRZSYALARSW1/D012l8nJhLe39vXrg2lssHdJVZlMKuSYALl1ZnsfL+Dzm8sC6DqloLsy/uwV3jIvD3kUWnhP2TABcua8uBQuITksk4Xs6oyHbMnRZDRIfWZpclxHmTABcuJ6+kigUrU1m5P5+Qtr68cfNgLo3uKN0lwuFIgAuXUV1n4c1vD/Lqpiy0hvsm9OL/RvfAx1NmUQrHJAEunJ7WmnWpJ3h8RQqHiyuZ1LcTD0+JIqStLDolHJsEuHBqBwvKmbc8hW8yCojo0JoP7hjGyIh2ZpclhE1IgAunVF5TzysbMlmy+RA+Hu48MiWKW0eE4enuZnZpQtiMBLhwKlprvtxzlAWrUjlRVsNVg0N4aGIf2vt7m12aEDYnAS6cRvLRUuITktmRfZL+IQG8fvNgBoW2NbssIZqNBLhweCcranl2TTr/2X6YQD8vFl7Zj2viuuEmi04JJycBLhyWxar5cPthnluTTll1Pbf8Loz7LulFgK/MohSuQQJcOKTE7GIe+zKZlPxTDO8RRPz0GPp0amN2WUK0KAlw4VBOnKrmn1+l8d/deXQO8GHRDQOZ0q+zzKIULkkCXDiE2nor73x/iJfXZ1Jn0fxlbAR/GtsTPy95CwvXJe9+Yfe+yShg3vJkDhZUMCGqA49OjaZ7cCuzyxLCdE0OcKWUO5AI5Gmtpza9JCEMR4ormb8ihbUpxwkL9uOdWUMY26eD2WUJYTds0QK/B0gF5AySsImqWguvbcri9W8P4uGm+NvE3tx+UTjeHrLolBCna1KAK6VCgCnAk8BfbVKRcFlaa75KOsYTK1I4WlrN9Ngu/GNyFJ0CfMwuTQi71NQW+IvA3wD/cz1AKTUbmA0QGhraxM0JZ5VxvIz4hGS2HCiiTyd/Xrh2AMN6BJtdlhB2rdEBrpSaCpzQWu9USo051+O01m8CbwLExcXpxm5POKdT1XW8uDaTd3/IprW3B4/PiOH6oaF4yKJTQvymprTARwLTlVKTAR+gjVLq31rrm2xTmnBmVqvm0125PP11GkUVtVw/NJQHLu1NUCsvs0sTwmE0OsC11n8H/g7Q0AJ/QMJbnI+9R0qYm5DMniMlDAoNZOltQ+nbNcDssoRwODIOXLSYwvIanvk6nY93HiG4lTfPXR3LFQO7yqJTQjSSTQJca70J2GSL1xLOp95i5f2tOTy/NoOqWgt3XBTO3eMj8feRRaeEaAppgYtmteVAIfEJyWQcL2dUZDvmTosmosM5By0JIS6ABLhoFkdLqnhyVSor9+UT0taX128azGUxHWXRKSFsSAJc2FR1nYXFmw+xaEMWVq25d0IkfxzdEx9PmUUphK1JgAub0FqzPvUEj69MIaeokstiOvLIlGi6BfmZXZoQTksCXDTZwYJyHl+Rwsb0Anq2b8X7tw9lVGR7s8sSwulJgItGq6ip55UNWSzefBBvD3cemRLFrSPC8JRZlEK0CAlwccG01iTsPcqCVakcP1XDzEEhPDSpNx38ZdEpIVqSBLi4IClHTxGfkMz27GL6dQ3g1RsHM7h7W7PLEsIlSYCL81JSWcvzazP499YcAv28+OeV/bgmrhvuMotSCNNIgItfZbFqlu04wjOr0yitquPm4d356yW9CfCTWZRCmE0CXJzTzpyTzE1IIinvFEPDg5g3PYaoznLhJSHshQS4+IUTp6pZ+HUan+/Ko1MbH16+fiDT+neWWZRC2BkJcPGT2norS7cc4uX1WdTWW/nTmJ78eWwErbzlbSKEPZLfTAHAtxkFxC9P5mBBBeP6dOCxqdGEtWtldllCiF8hAe7ijhRX8sTKFFYnHycs2I/Ft8YxPqqj2WUJIc6DBLiLqq6z8NqmA7z+zQHclOLBy3pzx6hwvD1k0SkhHIUEuIvRWrM6+RiPr0glr6SKabFd+MfkPnQO8DW7NCHEBZIAdyFZJ8qIT0hhc1YhfTr5858/DOd3PYPNLksI0UgS4C6grLqOl9ZlsnRLNn5e7sRPi+am4d3xkEWnhHBoEuBOzGrVfL47j4VfpVFUUcO1cd148LLeBLf2Nrs0IYQNSIA7qf25pTyWkMTuwyUM6BbIkllx9A8JNLssIYQNSYA7maLyGp5Znc6yxCMEt/Limav6M3NQCG6y6JQQTkcC3EnUW6x8sO0wz61Jp7LWwu0jw7l7QiRtfGTRKSGclQS4E9h6sIj4hGTSjpVxUUQ74qdHE9HB3+yyhBDNTALcgeWXVrFgVRrL9x6la6Avr980iMtiOsmiU0K4CAlwB1RTb+Ht7w6xaEMWFq25e3wkd47uia+XzKIUwpVIgDuY9anHmb8ihZyiSi6N7sijU6PpFuRndllCCBM0OsCVUt2A94COgAbe1Fq/ZKvCxM8dKqxg/vJkNqYX0KN9K977/VAu7tXe7LKEECZqSgu8Hrhfa71LKeUP7FRKrdVap9ioNgFU1NSzaGMWi787hJeHGw9PjuLWEWF4ecgsSiFcXaMDXGudD+Q3fF+mlEoFugIS4DagtWb5vnwWrEzl2KlqrhzUlTkT+9ChjY/ZpQkh7IRN+sCVUmHAQGCbLV7P1aXmnyI+IZlth4rp27UN/7pxIIO7B5ldlhDCzjQ5wJVSrYHPgHu11qfO8vPZwGyA0NDQpm7OqZVW1vH82nTe35pDgK8nC67ox7VDuuEusyiFEGfRpABXSnlihPcHWuvPz/YYrfWbwJsAcXFxuinbc1YWq2bZjiM8szqN0qo6bhrenb9e0otAPy+zSxNC2LGmjEJRwGIgVWv9vO1Kci07c04Sn5DM/rxShoYFET89hugubcwuSwjhAJrSAh8J3AzsV0rtabjvH1rrVU2uygWcKKtm4VdpfL4rj45tvHnpugFMj+0isyiFEOetKaNQNgOSNheozmJl6ffZvLQ+k5p6C3eO6clfxkbQylvmVAkhLoykRgv6LrOA+IRkDhRUMKZ3e+ZOiyG8XSuzyxJCOCgJ8BZwpLiSJ1em8nXyMboH+7H41jjGR3U0uywhhIOTAG9G1XUWXv/mAK9tOoCbUjxwaS/uGNUDH09ZdEoI0XQS4M1Aa83q5OM8sTKF3JNVTOnfmYcnR9El0Nfs0oQQTkQC3MayTpQxb3kK32UW0rujPx/+YRgjerYzuywhhBOSALeRsuo6Xl6fyTvfZ+Pn5U78tGhuGt4dD3dZdEoI0TwkwJvIatX8d3ceC79Oo7C8hmvjuvHAZb1p19rb7NKEEE5OArwJkvJKeezLJHYdLmFAt0DeviWO2G6BZpclhHAREuCNUFxRyzOr0/lox2GCW3nxzFX9mTkoBDdZdEoI0YIkwC9AvcXKh9sP89yaDMpr6rltRDj3XhJJGx9Ps0sTQrggCfDztO1gEXMTkkk7VsaInsHET4+hV0d/s8sSQrgwCfDfcKy0mgWrUknYe5Sugb68euMgJvXtdH6LTlnqoLYcrFbQFlBu4OkHnr4gi1YJV2C1Qs0psNb/7z53L/BqDW4yQqupJMDPoabewuLNh1i0IYt6q+bucRHcOSYCX6/TZlFa6qAgHY4nQWEmlOTAyRwoOwZVJ6G27ByvrsCnDfh3Nr4CukJwJLTvA+17Q9swCXjhGKwWKD4EJ5LhRKrx/i89YnxVnjTCm7NdBkCBtz/4BUNACLTpCkHh0CEKOkRDUA9wkxnLv0UC/Cw2pp9g/vIUDhVWcEl0Rx6dEk1osB9UFsPBHyBni/F1PAkstcaTlLsRxIHdofsI8AsC37bg1QrcPIzWt9ZQVwG1FVBVAmX5xldGMlT8+38F+AZByBDjq8do6DII3OVQCTtQV2W89w//AIe3Qt5OqKts+KEyGiSB3aDrYGjVHnwCwLsNeDQMq9UaLDVQUwbVp6DiBJTmQfZ3sG8ZP4W9ZysIGQzdhkHocOg+0vjkKn5Gad1yF8mJi4vTiYmJLba9C5VTVMH85SmsTztBj/atmDs1mtGBhZDxFWSsgdztoK3g7g0hccabtFN/6NQPgnuCexNOZladhIIMoyWTuxPyEqEgzfiZdwCEj4Lek6H3JOOPgxAtpTQX0lZC5hrI3gz11UaDpFM/I2A7xxqt5vZ9wMuv8duprYTCdDieAvl74cg2OLbf6H708DFCPPISiJpmtNpdiFJqp9Y67hf3S4BDZW09/9qYxVvfHsLTXfHI77y52mc7Himf/y9EO8dC5GXQc6zRIvZsgavDVxbDoW/gwEbIWgen8oyWfvgo6Hc1RM8wPoYKYWtlxyHpM0j+r9FwAQiOgIhLIGIChA5rmfdeTbnR0j+w3vgdKMww7g8ZAtGXG78H/s6/sqcE+FlorVmxL58Fq1IpKS3h0fBMrnLbgFfeNkAZXSExV0CfqdCms9nFwtHdkJoAKV9C8UHjhGjUNBh0i9E6kX5z0RSWOqOVvfvfkLHaaPl26mcEZfTl0C7C7Aqh6ACkfAHJX8CxfUaDJvISGHgT9JrYtE/BdkwC/Axpx04Rn5DM0UOp3NdmE9OsG/CoK4OgnjDoZuh3jdGnbY+0htwdsOdDSPocakqNj69D7oDY66RVLi5MeQHsWgo7lkDZUWjdEWKvhwE3QvteZld3bgUZsOcD2PsRlB8D/y4Q93sYfCu07mB2dTYlAd6gtLKOF9ZlkLJtNXd6rmQMO8HNHRU9A+JuN1rdjtSSra2E5M9h+1uQv8foL4+bBcP+CG26mF2dsGcF6bDlZdj3sXEyvsdYGDobIi91rJPmlnrjk8OOt+DABmOYYv9rYMTdxqguJ+DyAW6xaj7ZcZgtq//DLfWfEeeWgdU3CLchtxt/tZ0h7HITYcsrRjeLcof+18KovxonWIX4Ue5O+O45SF8JHr4w4AbjD749t7bPV2EmbHsddn8A9VXQaxJc/IAx6MCBuXSA784pZsWn73B56Xv0c8umtnVXvEbdY/SbeTnhNSmLD8EP/4Ld7xstq35Xw8UPQrtIsysTZjqyHTYtNE4I+rY1WttDZ0MrJ1yvvqLIaJFve90Y4dVzPIyZA92Gml1Zo7hkgBecqua/n73PsEOvEut2kAq/bvhNeAgVe53Tnuz4mbLjxkfkxCXG0K/Y62H0Q9C2u9mViZaUvxfWzzdGcfgFw4i7YMgfwLu12ZU1v5oy2LHY+D2oLDJG0Ix7FLoMMLuyC+JSAV5nsfLV18vptP0phqpkSrw74zPh7/gMusE1gvtMFYWw+QWjn1xbYfAsI8hbtze7MtGcig7AhseNoYC+bWHkvcaJblcI7jPVVsCOt43fg6qTxqiacY/ax8ia8+AyAb5zdyIVKx/l4votlLoFUnvRg7S/eDZ4eDXrdh3CqaPwzdOw6z1jVtvIe+B3f3bObiRXVl4A3zwFO98xJp397k9Gq9snwOzKzFddanQv/vAvY1Zp3G0weo7dN2acPsCPHs0lbdmjjCr5kjrlydGYP9Bz+kMoGVL3S4WZsH4epC6H1p1g/KNG94qsPeHY6qqMYNr8ojG9ffAso9/XyYbU2UT5CeOPXOI7RmPmovuMxoydTtd32gCvrq5m27KnGXDwdVpTSWrny4m4dgE+bZ1gVElzO7wN1jxsjCnv1A8u+6cxy1M4Fq2NoaRr5xqLSPWeDBPmOceokuZWmGn8v6WvhIBuMCEe+s60u6HEThfgWmt2rf+Ydt/Po7vOI9UvjqArn6FjxCCbvL7L0NqYMr0u3vjlj5oOlz5urIgo7N/R3fDVHDiyFTr2g4kLIPxis6tyPIe+hdX/MNZe6TYMJj0FXQaaXdVPnCrAD2fupfizBxhQvZ0jbl0pv3geUaOvsru/mg6lrgq2LILNzxtLhI64yxhDLv3j9qm8wOgG2/1vYxjguEeNYbHSDdZ4Vosxs3P9fOPE/8CbYPxjdtEF1SwBrpSaCLwEuANva60X/trjmxrg5adOkvThwwzK/4ha5UVK5J0MvPohPL1aYGEpV1GaB+vmwv5PjDWaL5lvlx8pXZalzhhNtOmfRj/3sD/C6L/JCUpbqi6Fb5+Bra8bfeJj5hjj5U0cwWbzAFdKuQMZwCVALrADuF5rnXKu5zQ2wLXVys4VrxO26ynaUcKOwMn0uP5pgjt2a1Tt4jwc3gqrHjQWDOo+EiY9DZ36ml2Vazu4Cb56yFghs+d4mLhQ+rmbU2EmfD3HGD/frrfRrdJzrCmlnCvAm3JNo6FAltb6oNa6FvgImNGE1zunPc9OIW7X3yn26EDG9C8Zcu9/JLybW+hwmL0Jpr5gXGnljVFGoFcWm12Z6zmZA8tugvdmGBOyrvsP3PSZhHdzaxcJN34K139kXITi/cuN41By2OzKftKUFvhVwESt9R0Nt28Ghmmt/3LG42YDswFCQ0MH5+TkXPC2dnz5GlhqGTzjL7i5Sx9fi6ssho1PGjM6fQKNfsFBt0h/a3Orq4LvXzImn6Bg1P3GuYmWWIte/FxdNfzwCnz3vDEZ7qL7jHkULTTssDm6UM4rwE9nD6sRiiY4th9W/Q0ObzGuRDT5GaOlLmxLa2NBstWPQOlhY9bgpU8YlyoT5irNhTWPGsM2A0LhsieMkVvNfI6oObpQ8oDT31EhDfcJZ9WpH9y2Cq5aYqwrseQy+OwO48SnsI3jyfDedPj4FmNd91tXwDXvSnjbi4AQuPodmLXSuDD5x7fAu9PgWJIp5TSlBe6BcRJzPEZw7wBu0Fonn+s50gJ3IrUVxkf77182ulJG3mt8vG/KNRFdWUWh0U21c6lxEeBxj8Dg2xxrXW5XY6k3livY+KQxcmXwLBj7cLOs7thcwwgnAy9iDCNcorV+8tceLwHuhE7mwNpHjcu8tekK4+cay9e6NeXDnQupr4Ftb8C3z0JtubHY1Jg5cuFqR1JZbEzL3/4WeLWGi++Hof9n03MVTjWRR9ih7O+NmWz5e4wZbJc+AWEXmV2V/dLaWCVw3VxjVEPEBLj0SejQx+zKRGMVpMOaR4yrAwWGGtPyY660Sf+4BLhoflarMQFo/Tw4lWdcmmtCPHSMMbsy+3LwG2PpgqO7oEOMcSKs5zizqxK2cmCjEeTHk4zGzIR46DGmSS8pAS5aTl2V0S2w+XmoPmVcn3D0Q3Jpt7xdxvrcBzZAmxAY+3dZBdJZWS3GtUY3PmmsMdRzHEx5HoLCG/VyEuCi5VUW/+9CEpZaGHgjjHrA9a4IlL/PmPqevsq4sMKo+40r4sh4budXV21cSGL7G/CHjY0+wSkBLsxTdtxojScuMSZB9L/OmAjhIFdDabQjO4z9Tl9lrFUy4i7j5JZPG7MrEy3NamnSJy0JcGG+0jzY8oox9MpSa0yAGHGXw18x/GesVqOL5PsXIfs7o8U97E4Y/kdZcEo0mgS4sB/lJ4wrxyS+AzWl0G24EXB9pjruNUtrK2DfMtj6GhRmGFc6GnGXMTbYFa9BKWxKAlzYn5pyYz3rra9CSQ607ggDbzbWWXGUfvL8fcbkm/2fQM0p6DzAuDRX9OVyHVZhMxLgwn5ZLZC1HhIXQ8ZqQBtL2Pa/BqJnGN0Q9qQ0z7iK0f5PjOV2PXyMwI67zbiai6ydLmxMAlw4hpIjsO8j2LsMijLBzQPCRkHUVOg1CQK6tnxNWhvdIulfQcbXxlrpaOg6GPpfa/yhsbc/MsKpSIALx6K1MdEl5UtIXQHFB4z72/UyJkWEjTICtE0X27d4tYaT2UZQZ2+G7G//twZ0p/5GX32/q2Rcu2gxEuDCcWltTFPOWmtclSZni3E5MTBOFnYZYCy+364XBPUE/07G169dz1Nro8+6otAI65OHoOggHN8P+XuNxYnAaFl3H2lciaXXRGM1OiFa2LkCXJY6E/ZPKWONkA59jJEd9bVGyB7dZcxuzN9rDN2z1P78eZ5+Roh7+IKHN2iLcU3J+mqoKgFr3c8f7+EDHaIg5groHAshQ6FDtCzMJeyWBLhwPB5e0G2I8fUjq8Xo5ig+aAxTLD8OFQVGS72uyghtNw9w9zKGKvq2Bb924BdsLDwUFG605iWshQORABfOwc3dCOFGrjUhhCOS5oYQQjgoCXAhhHBQEuBCCOGgJMCFEMJBSYALIYSDkgAXQggHJQEuhBAOSgJcCCEcVIuuhaKUKgByGvn0dkChDctxFK643664z+Ca+y37fH66a63bn3lniwZ4UyilEs+2mIuzc8X9dsV9Btfcb9nnppEuFCGEcFAS4EII4aAcKcDfNLsAk7jifrviPoNr7rfscxM4TB+4EEKIn3OkFrgQQojTSIALIYSDcogAV0pNVEqlK6WylFJzzK6nOSiluimlNiqlUpRSyUqpexruD1JKrVVKZTb863SXP1dKuSuldiulVjTcDldKbWs43suUUl5m12hrSqlApdSnSqk0pVSqUup3zn6slVL3Nby3k5RS/1FK+TjjsVZKLVFKnVBKJZ1231mPrTK83LD/+5RSgy5kW3Yf4Eopd+BfwCQgGrheKRVtblXNoh64X2sdDQwH/tywn3OA9VrrSGB9w21ncw+Qetrtp4AXtNYRwEngdlOqal4vAV9rrfsAsRj777THWinVFbgbiNNa9wXcgetwzmO9FJh4xn3nOraTgMiGr9nAaxeyIbsPcGAokKW1Pqi1rgU+AmaYXJPNaa3ztda7Gr4vw/iF7oqxr+82POxd4HJTCmwmSqkQYArwdsNtBYwDPm14iDPucwBwMbAYQGtdq7UuwcmPNcYlHH2VUh6AH5CPEx5rrfW3QPEZd5/r2M4A3tOGrUCgUqrz+W7LEQK8K3DktNu5Dfc5LaVUGDAQ2AZ01FrnN/zoGNDRrLqayYvA3wBrw+1goERrXd9w2xmPdzhQALzT0HX0tlKqFU58rLXWecCzwGGM4C4FduL8x/pH5zq2Tco3Rwhwl6KUag18BtyrtT51+s+0MebTacZ9KqWmAie01jvNrqWFeQCDgNe01gOBCs7oLnHCY90Wo7UZDnQBWvHLbgaXYMtj6wgBngd0O+12SMN9Tkcp5YkR3h9orT9vuPv4jx+pGv49YVZ9zWAkMF0plY3RNTYOo284sOFjNjjn8c4FcrXW2xpuf4oR6M58rCcAh7TWBVrrOuBzjOPv7Mf6R+c6tk3KN0cI8B1AZMPZai+MEx8JJtdkcw19v4uBVK3186f9KAG4teH7W4EvW7q25qK1/rvWOkRrHYZxXDdorW8ENgJXNTzMqfYZQGt9DDiilOrdcNd4IAUnPtYYXSfDlVJ+De/1H/fZqY/1ac51bBOAWxpGowwHSk/ravltWmu7/wImAxnAAeBhs+tppn28CONj1T5gT8PXZIw+4fVAJrAOCDK71mba/zHAiobvewDbgSzgE8Db7PqaYX8HAIkNx/sLoK2zH2tgHpAGJAHvA97OeKyB/2D089dhfNq6/VzHFlAYo+wOAPsxRumc97ZkKr0QQjgoR+hCEUIIcRYS4EII4aAkwIUQwkFJgAshhIOSABdCCAclAS6EEA5KAlwIIRzU/wOkbX6URZiiTQAAAABJRU5ErkJggg==\n" + "text/plain": "
" }, "metadata": { "needs_background": "light" - } + }, + "output_type": "display_data" } + ], + "source": [ + "ax = df.plot()" ] } - ] + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.3 64-bit ('base': conda)", + "metadata": { + "interpreter": { + "hash": "61a9efb557ee20b19fda2d58cb63f9a3bf86c2530fcd43d63aa6e0adea42a5e4" + } + }, + "name": "Python 3.8.3 64-bit ('base': conda)" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.3-final" + }, + "orig_nbformat": 2 + }, + "nbformat": 4, + "nbformat_minor": 2 } \ No newline at end of file diff --git a/tests/test_builds.py b/tests/test_builds.py index ab6960f..ca3e16a 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -44,6 +44,7 @@ # ########## Helpers ############### # ################################## + @contextmanager def working_directory(path): """ @@ -77,14 +78,9 @@ def get_plugin_config_from_mkdocs(mkdocs_path) -> dict: logging.info("Fixture configuration loaded: " + str(cfg)) if plugin_loaded.config.get("enabled"): - assert ( - plugin_loaded.config.get("locale") is not None - ), "Locale should never be None after plugin is loaded" + assert plugin_loaded.config.get("locale") is not None, "Locale should never be None after plugin is loaded" - logging.info( - "Locale '%s' determined from %s" - % (plugin_loaded.config.get("locale"), mkdocs_path) - ) + logging.info("Locale '%s' determined from %s" % (plugin_loaded.config.get("locale"), mkdocs_path)) return plugin_loaded.config @@ -121,7 +117,7 @@ def setup_clean_mkdocs_folder(mkdocs_yml_path, output_path): shutil.copytree("tests/fixtures/i18n/docs", str(testproject_path / "docs")) else: shutil.copytree("tests/fixtures/basic_project/docs", str(testproject_path / "docs")) - + shutil.copyfile(mkdocs_yml_path, str(testproject_path / "mkdocs.yml")) if "gen-files" in mkdocs_yml_path: @@ -155,24 +151,28 @@ def setup_commit_history(testproject_path): repo.git.checkout("-b", "master") author = "Test Person " - with working_directory(testproject_path): - # page_with_tags contains tags we replace and test if os.path.exists("docs/page_with_tag.md"): repo.git.add("docs/page_with_tag.md") - repo.git.commit(message="add homepage", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000 + repo.git.commit( + message="add homepage", author=author, date="1500854705" + ) # Mon Jul 24 2017 00:05:05 GMT+0000 file_name = os.path.join(testproject_path, "docs/page_with_tag.md") with open(file_name, "a") as the_file: the_file.write("test\n") repo.git.add("docs/page_with_tag.md") - repo.git.commit(message="update homepage #1", author=author, date="1525475836") # Fri May 04 2018 23:17:16 GMT+0000 + repo.git.commit( + message="update homepage #1", author=author, date="1525475836" + ) # Fri May 04 2018 23:17:16 GMT+0000 with open(file_name, "a") as the_file: the_file.write("awa\n") repo.git.add("docs/page_with_tag.md") - repo.git.commit(message="update homepage #2", author=author, date="1642911026") # Sun Jan 23 2022 04:10:26 GMT+0000 + repo.git.commit( + message="update homepage #2", author=author, date="1642911026" + ) # Sun Jan 23 2022 04:10:26 GMT+0000 if os.path.exists("docs/page_with_renamed.md"): bf_file_name = os.path.join(testproject_path, "docs/page_with_renamed.md") @@ -181,32 +181,44 @@ def setup_commit_history(testproject_path): if os.path.exists(af_file_name): os.replace(af_file_name, bf_file_name) repo.git.add("docs/page_with_renamed.md") - repo.git.commit(message="page_with_renamed.md before renamed", author=author, date="1655229469") # Tue Jun 14 2022 17:57:49 GMT+0000 + repo.git.commit( + message="page_with_renamed.md before renamed", author=author, date="1655229469" + ) # Tue Jun 14 2022 17:57:49 GMT+0000 repo.git.mv("docs/page_with_renamed.md", "docs/subfolder/page_with_renamed.md") - repo.git.commit(message="page_with_renamed.md after renamed", author=author, date="1655229515") # Tue Jun 14 2022 17:58:35 GMT+0000 + repo.git.commit( + message="page_with_renamed.md after renamed", author=author, date="1655229515" + ) # Tue Jun 14 2022 17:58:35 GMT+0000 if os.path.exists("docs/first_page.md"): repo.git.add("docs/first_page.md") - repo.git.commit(message="first page", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000 + repo.git.commit(message="first page", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000 file_name = os.path.join(testproject_path, "docs/first_page.md") with open(file_name, "w+") as the_file: the_file.write("Hello\n") repo.git.add("docs/first_page.md") - repo.git.commit(message="first page update 1", author=author, date="1519964705") # Fri Mar 02 2018 04:25:05 GMT+0000 + repo.git.commit( + message="first page update 1", author=author, date="1519964705" + ) # Fri Mar 02 2018 04:25:05 GMT+0000 with open(file_name, "w") as the_file: the_file.write("# First Test Page Edited\n\nSome Lorem text") repo.git.add("docs/first_page.md") - repo.git.commit(message="first page update 2", author=author, date="1643911026") # Thu Feb 03 2022 17:57:06 GMT+0000 + repo.git.commit( + message="first page update 2", author=author, date="1643911026" + ) # Thu Feb 03 2022 17:57:06 GMT+0000 repo.git.add("mkdocs.yml") - repo.git.commit(message="add mkdocs", author=author, date="1500854705 -0700") # Mon Jul 24 2017 00:05:05 GMT+0000 + repo.git.commit( + message="add mkdocs", author=author, date="1500854705 -0700" + ) # Mon Jul 24 2017 00:05:05 GMT+0000 if os.path.exists("docs/second_page.md"): repo.git.add("docs/second_page.md") - repo.git.commit(message="second page", author=author, date="1643911026") # Thu Feb 03 2022 17:57:06 GMT+0000 - + repo.git.commit( + message="second page", author=author, date="1643911026" + ) # Thu Feb 03 2022 17:57:06 GMT+0000 + repo.git.add("docs/index.md") - repo.git.commit(message="homepage", author=author, date="1643911026") # Thu Feb 03 2022 17:57:06 GMT+0000 + repo.git.commit(message="homepage", author=author, date="1643911026") # Thu Feb 03 2022 17:57:06 GMT+0000 return repo @@ -251,21 +263,21 @@ def validate_build(testproject_path, plugin_config: dict = {}): # Make sure with markdown tag has valid # git revision date tag - if not plugin_config.get('enabled'): - return - + if not plugin_config.get("enabled"): + return + page_with_tag = testproject_path / "site/page_with_tag/index.html" contents = page_with_tag.read_text(encoding="utf8") assert re.search(r"renders as\:\s[|\w].+", contents) repo = Util(config=plugin_config, mkdocs_dir=testproject_path) - commit_hash, commit_timestamp =repo.get_git_commit_timestamp( - path=str(testproject_path / "docs/page_with_tag.md"), - is_first_commit=False, - ) + commit_hash, commit_timestamp = repo.get_git_commit_timestamp( + path=str(testproject_path / "docs/page_with_tag.md"), + is_first_commit=False, + ) date_formats = repo.get_date_formats_for_timestamp( commit_timestamp, - locale=plugin_config['locale'], + locale=plugin_config["locale"], add_spans=True, ) @@ -273,11 +285,14 @@ def validate_build(testproject_path, plugin_config: dict = {}): assert any(searches), "No correct revision date formats output was found" if plugin_config.get("enable_creation_date"): - commit_hash, commit_timestamp = repo.get_git_commit_timestamp(path=str(testproject_path / "docs/page_with_tag.md"),is_first_commit=True,) + commit_hash, commit_timestamp = repo.get_git_commit_timestamp( + path=str(testproject_path / "docs/page_with_tag.md"), + is_first_commit=True, + ) assert commit_timestamp == 1500854705 date_formats = repo.get_date_formats_for_timestamp( commit_timestamp=commit_timestamp, - locale=plugin_config['locale'], + locale=plugin_config["locale"], add_spans=True, ) @@ -285,12 +300,12 @@ def validate_build(testproject_path, plugin_config: dict = {}): assert any(searches), "No correct creation date formats output was found" if os.path.exists(str(testproject_path / "docs/subfolder/page_with_renamed.md")): - commit_hash, commit_timestamp=repo.get_git_commit_timestamp( - path=str(testproject_path / "docs/subfolder/page_with_renamed.md"), - is_first_commit=True + commit_hash, commit_timestamp = repo.get_git_commit_timestamp( + path=str(testproject_path / "docs/subfolder/page_with_renamed.md"), is_first_commit=True ) assert commit_timestamp == 1655229469 + def validate_mkdocs_file(temp_path: str, mkdocs_yml_file: str): """ Creates a clean mkdocs project @@ -300,50 +315,45 @@ def validate_mkdocs_file(temp_path: str, mkdocs_yml_file: str): temp_path (PosixPath): Path to temporary folder mkdocs_yml_file (PosixPath): Path to mkdocs.yml file """ - testproject_path = setup_clean_mkdocs_folder( - mkdocs_yml_path=mkdocs_yml_file, output_path=temp_path - ) + testproject_path = setup_clean_mkdocs_folder(mkdocs_yml_path=mkdocs_yml_file, output_path=temp_path) setup_commit_history(testproject_path) result = build_docs_setup(testproject_path) assert result.exit_code == 0, f"'mkdocs build' command failed with output:\n{result.stdout}" # validate build with locale retrieved from mkdocs config file - validate_build( - testproject_path, plugin_config=get_plugin_config_from_mkdocs(mkdocs_yml_file) - ) + validate_build(testproject_path, plugin_config=get_plugin_config_from_mkdocs(mkdocs_yml_file)) return testproject_path - MKDOCS_FILES = [ - 'basic_project/mkdocs_creation_date.yml', - 'basic_project/mkdocs_custom_type.yml', - 'basic_project/mkdocs_datetime.yml', - 'basic_project/mkdocs_exclude.yml', - 'basic_project/mkdocs_fallback_to_build_date.yml', - 'basic_project/mkdocs_locale.yml', - 'basic_project/mkdocs_meta.yml', - 'basic_project/mkdocs_plugin_locale.yml', - 'basic_project/mkdocs.yml', - 'basic_project/mkdocs_theme_timeago_locale.yml', - 'basic_project/mkdocs_theme_language.yml', - 'basic_project/mkdocs_theme_locale_and_language.yml', - 'basic_project/mkdocs_theme_locale_disabled.yml', - 'basic_project/mkdocs_theme_timeago.yml', - 'basic_project/mkdocs_theme_locale.yml', - 'basic_project/mkdocs_theme_no_locale.yml', - 'basic_project/mkdocs_theme_timeago_override.yml', - 'basic_project/mkdocs_theme_timeago_instant.yml', - 'basic_project/mkdocs_timeago_locale.yml', - 'basic_project/mkdocs_timeago.yml', - 'basic_project/mkdocs_with_override.yml', + "basic_project/mkdocs_creation_date.yml", + "basic_project/mkdocs_custom_type.yml", + "basic_project/mkdocs_datetime.yml", + "basic_project/mkdocs_exclude.yml", + "basic_project/mkdocs_fallback_to_build_date.yml", + "basic_project/mkdocs_locale.yml", + "basic_project/mkdocs_meta.yml", + "basic_project/mkdocs_plugin_locale.yml", + "basic_project/mkdocs.yml", + "basic_project/mkdocs_theme_timeago_locale.yml", + "basic_project/mkdocs_theme_language.yml", + "basic_project/mkdocs_theme_locale_and_language.yml", + "basic_project/mkdocs_theme_locale_disabled.yml", + "basic_project/mkdocs_theme_timeago.yml", + "basic_project/mkdocs_theme_locale.yml", + "basic_project/mkdocs_theme_no_locale.yml", + "basic_project/mkdocs_theme_timeago_override.yml", + "basic_project/mkdocs_theme_timeago_instant.yml", + "basic_project/mkdocs_timeago_locale.yml", + "basic_project/mkdocs_timeago.yml", + "basic_project/mkdocs_with_override.yml", # 'i18n/mkdocs.yml' ] INVALID_MKDOCS_FILES = [ - ('basic_project/mkdocs_unknown_type.yml', "AssertionError"), - ('i18n/mkdocs_wrong_order.yml', "should be defined after the i18n plugin in your mkdocs.yml"), + ("basic_project/mkdocs_unknown_type.yml", "AssertionError"), + ("i18n/mkdocs_wrong_order.yml", "should be defined after the i18n plugin in your mkdocs.yml"), ] @@ -357,27 +367,23 @@ def test_tags_are_replaced(tmp_path, mkdocs_file): """ Make sure the {{ }} tags are replaced properly. """ - testproject_path = setup_clean_mkdocs_folder( - mkdocs_yml_path=f"tests/fixtures/{mkdocs_file}", output_path=tmp_path - ) + testproject_path = setup_clean_mkdocs_folder(mkdocs_yml_path=f"tests/fixtures/{mkdocs_file}", output_path=tmp_path) setup_commit_history(testproject_path) result = build_docs_setup(testproject_path) assert result.exit_code == 0, "'mkdocs build' command failed" - plugin_config=get_plugin_config_from_mkdocs(str(testproject_path / "mkdocs.yml")) + plugin_config = get_plugin_config_from_mkdocs(str(testproject_path / "mkdocs.yml")) tags_file = testproject_path / "site/page_with_tag/index.html" contents = tags_file.read_text(encoding="utf8") # validate the build - validate_build( - testproject_path, plugin_config=plugin_config - ) + validate_build(testproject_path, plugin_config=plugin_config) if plugin_config.get("enabled") == False: return True if plugin_config.get("type") == "timeago": - pytest.skip("Not necessary to test the JS library") + pytest.skip("Not necessary to test the JS library") # Make sure count_commits() works # We created 11 commits in setup_commit_history() @@ -385,62 +391,56 @@ def test_tags_are_replaced(tmp_path, mkdocs_file): u = Util(config={}, mkdocs_dir=os.getcwd()) assert commit_count(u._get_repo("docs/page_with_tag.md")) == 11 - # the revision date was in 'setup_commit_history' was set to 1642911026 (Sun Jan 23 2022 04:10:26 GMT+0000) # Assert {{ git_revision_date_localized }} is replaced - date_formats_revision_date = get_date_formats(1642911026, + date_formats_revision_date = get_date_formats( + 1642911026, locale=plugin_config.get("locale"), time_zone=plugin_config.get("timezone"), - custom_format=plugin_config.get("custom_format") + custom_format=plugin_config.get("custom_format"), ) for k, v in date_formats_revision_date.items(): assert v is not None - - date = date_formats_revision_date.get(plugin_config.get('type')) + + date = date_formats_revision_date.get(plugin_config.get("type")) assert re.search(rf"{date}\<\/span.+", contents) # The last site revision was set in setup_commit_history to 1643911026 (Thu Feb 03 2022 17:57:06 GMT+0000) # Assert {{ git_site_revision_date_localized }} is replaced - date_formats_revision_date = get_date_formats(1643911026, + date_formats_revision_date = get_date_formats( + 1643911026, locale=plugin_config.get("locale"), time_zone=plugin_config.get("timezone"), - custom_format=plugin_config.get("custom_format") + custom_format=plugin_config.get("custom_format"), ) for k, v in date_formats_revision_date.items(): assert v is not None - date = date_formats_revision_date.get(plugin_config.get('type')) + date = date_formats_revision_date.get(plugin_config.get("type")) assert re.search(rf"{date}\<\/span.+", contents) # Note {{ git_creation_date_localized }} is only replaced when configured in the config if plugin_config.get("enable_creation_date"): # The creation of page_with_tag.md was set in setup_commit_history to 1500854705 ( Mon Jul 24 2017 00:05:05 GMT+0000 ) - date_formats_revision_date = get_date_formats(1500854705, + date_formats_revision_date = get_date_formats( + 1500854705, locale=plugin_config.get("locale"), time_zone=plugin_config.get("timezone"), - custom_format=plugin_config.get("custom_format") + custom_format=plugin_config.get("custom_format"), ) for k, v in date_formats_revision_date.items(): assert v is not None - date = date_formats_revision_date.get(plugin_config.get('type')) + date = date_formats_revision_date.get(plugin_config.get("type")) assert re.search(rf"{date}\<\/span.+", contents) - - - - def test_git_not_available(tmp_path, recwarn): """ When there is no GIT repo, this should fail """ - testproject_path = setup_clean_mkdocs_folder( - "tests/fixtures/basic_project/mkdocs.yml", tmp_path - ) + testproject_path = setup_clean_mkdocs_folder("tests/fixtures/basic_project/mkdocs.yml", tmp_path) result = build_docs_setup(testproject_path) - assert ( - result.exit_code == 1 - ), "'mkdocs build' command succeeded while there is no GIT repo" + assert result.exit_code == 1, "'mkdocs build' command succeeded while there is no GIT repo" # assert there's a no error when fallback to build date is set to true testproject_path = setup_clean_mkdocs_folder( @@ -450,16 +450,12 @@ def test_git_not_available(tmp_path, recwarn): assert result.exit_code == 0 - - def test_build_material_theme(tmp_path): """ When using mkdocs-material theme, test correct working """ # theme set to 'material' with 'language' set to 'de' - testproject_path = validate_mkdocs_file( - tmp_path, "tests/fixtures/basic_project/mkdocs_theme_language.yml" - ) + testproject_path = validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_theme_language.yml") # In mkdocs-material, a 'last update' should appear # in German because locale is set to 'de' @@ -473,9 +469,7 @@ def test_material_theme_locale(tmp_path): When using mkdocs-material theme, test correct working """ # theme set to 'material' with 'locale' set to 'de' - testproject_path = validate_mkdocs_file( - tmp_path, "tests/fixtures/basic_project/mkdocs_theme_locale.yml" - ) + testproject_path = validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_theme_locale.yml") # In mkdocs-material, a 'last update' should appear # in english instead of German because you should use 'language' and not locale. @@ -490,9 +484,7 @@ def test_material_theme_locale_disabled(tmp_path): When using mkdocs-material theme, test correct working """ # theme set to 'material' with 'locale' set to 'de' - testproject_path = validate_mkdocs_file( - tmp_path, "tests/fixtures/basic_project/mkdocs_theme_locale_disabled.yml" - ) + testproject_path = validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_theme_locale_disabled.yml") # In mkdocs-material, a 'last update' should appear # in english instead of German because you should use 'language' and not locale. @@ -507,9 +499,7 @@ def test_material_theme_no_locale(tmp_path): When using mkdocs-material theme, test correct working """ # theme set to 'material' with 'language' set to 'de' - testproject_path = validate_mkdocs_file( - tmp_path, "tests/fixtures/basic_project/mkdocs_theme_no_locale.yml" - ) + testproject_path = validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_theme_no_locale.yml") # In mkdocs-material, a 'last update' should appear # in english because default locale is set to 'en' @@ -518,15 +508,14 @@ def test_material_theme_no_locale(tmp_path): assert re.search(r"Last update", contents) - @pytest.mark.parametrize("mkdocs_file, error", INVALID_MKDOCS_FILES) def test_type_unknown(mkdocs_file, error, tmp_path): """ Make sure invalid mkdocs.yml specification raise the correct errors. """ testproject_path = setup_clean_mkdocs_folder( - mkdocs_yml_path=f"tests/fixtures/{ mkdocs_file }", # mkdocs_file, # tmp_path, , - output_path=tmp_path + mkdocs_yml_path=f"tests/fixtures/{mkdocs_file}", # mkdocs_file, # tmp_path, , + output_path=tmp_path, ) # Setup git commit history assert not os.path.exists(str(testproject_path / ".git")) @@ -538,7 +527,7 @@ def test_type_unknown(mkdocs_file, error, tmp_path): with working_directory(testproject_path): # page_with_tags contains tags we replace and test repo.git.add(".") - repo.git.commit(message="add all", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000 + repo.git.commit(message="add all", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000 result = build_docs_setup(testproject_path) assert result.exit_code == 1 @@ -546,16 +535,12 @@ def test_type_unknown(mkdocs_file, error, tmp_path): assert error in result.stdout or error in str(result.exc_info[0]) - - def test_exclude_pages(tmp_path): """ When using mkdocs-material theme, test correct working """ # theme set to 'material' with 'locale' set to 'de' - testproject_path = validate_mkdocs_file( - tmp_path, "tests/fixtures/basic_project/mkdocs_exclude.yml" - ) + testproject_path = validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_exclude.yml") # Make sure revision date does not exist in excluded pages first_page = testproject_path / "site/first_page/index.html" @@ -567,16 +552,13 @@ def test_exclude_pages(tmp_path): assert not re.search(r"Last update\:\s[', - "custom": "01. January 1970" + "custom": "01. January 1970", } assert get_date_formats(0) == expected_output @@ -22,7 +21,7 @@ def test_get_dates(): new_expected_output = expected_output.copy() new_expected_output["timeago"] = '' assert get_date_formats(0, locale="en_US") == new_expected_output - + # Test with different locale expected_output = { "date": "1 janvier 1970", @@ -30,7 +29,7 @@ def test_get_dates(): "iso_date": "1970-01-01", "iso_datetime": "1970-01-01 00:00:00", "timeago": '', - "custom": "01. janvier 1970" + "custom": "01. janvier 1970", } assert get_date_formats(0, locale="fr") == expected_output @@ -41,7 +40,7 @@ def test_get_dates(): "iso_date": "1970-01-01", "iso_datetime": "1970-01-01 00:00:00", "timeago": '', - "custom": "01. janeiro 1970" + "custom": "01. janeiro 1970", } assert get_date_formats(0, locale="pt_BR") == expected_output @@ -56,7 +55,7 @@ def test_get_dates(): "iso_date": "1970-01-01", "iso_datetime": "1970-01-01 00:00:00", "timeago": '', - "custom": "01. Jan 1970" + "custom": "01. Jan 1970", } assert get_date_formats(0, locale="en", time_zone="UTC", custom_format="%d. %b %Y") == expected_output @@ -67,7 +66,7 @@ def test_get_dates(): "iso_date": "1970-01-01", "iso_datetime": "1970-01-01 02:00:00", "timeago": '', - "custom": "01. January 1970" + "custom": "01. January 1970", } loc_dt = datetime(1970, 1, 1, 1, 0, 0, tzinfo=get_timezone("Europe/Berlin")) unix_timestamp = loc_dt.replace(tzinfo=timezone.utc).timestamp() @@ -75,7 +74,7 @@ def test_get_dates(): # Test with missing arguments with pytest.raises(TypeError): - get_date_formats() # noqa + get_date_formats() # noqa # Test with invalid timezone with pytest.raises(LookupError): @@ -83,21 +82,20 @@ def test_get_dates(): # Test with more recent date expected_output = { - 'date': 'October 15, 2023', - 'datetime': 'October 15, 2023 13:32:04', - 'iso_date': '2023-10-15', - 'iso_datetime': '2023-10-15 13:32:04', - 'timeago': '', - 'custom': '15. October 2023' + "date": "October 15, 2023", + "datetime": "October 15, 2023 13:32:04", + "iso_date": "2023-10-15", + "iso_datetime": "2023-10-15 13:32:04", + "timeago": '', + "custom": "15. October 2023", } assert get_date_formats(1697369524, time_zone="Europe/Amsterdam") == expected_output - assert get_date_formats(1582397529) == { "date": "February 22, 2020", "datetime": "February 22, 2020 18:52:09", "iso_date": "2020-02-22", "iso_datetime": "2020-02-22 18:52:09", "timeago": '', - "custom": '22. February 2020', - } \ No newline at end of file + "custom": "22. February 2020", + } diff --git a/tests/test_exclude.py b/tests/test_exclude.py index dd19cc9..7f87d3b 100644 --- a/tests/test_exclude.py +++ b/tests/test_exclude.py @@ -1,8 +1,8 @@ from mkdocs_git_revision_date_localized_plugin.exclude import exclude import pytest -def test_exclude(): +def test_exclude(): with pytest.raises(AssertionError): exclude("fsdfs", "not a list") @@ -16,4 +16,4 @@ def test_exclude(): globs = ["folder/*"] assert exclude("folder/index.md", globs) - assert not exclude("subfolder/index.md", globs) \ No newline at end of file + assert not exclude("subfolder/index.md", globs) diff --git a/tests/test_parse_git_ignore_revs.py b/tests/test_parse_git_ignore_revs.py index f22d9fd..7ada34a 100644 --- a/tests/test_parse_git_ignore_revs.py +++ b/tests/test_parse_git_ignore_revs.py @@ -9,9 +9,10 @@ ("\n\n\n\n\nabc123\n\n\n\n\n", ["abc123"]), ] + @pytest.mark.parametrize("test_input,expected", TEST_PARAMS) def test_parse_git_ignore_revs(test_input, expected): - with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', delete=False) as fp: + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as fp: fp.write(test_input) temp_file_name = fp.name try: From de5f9f04b914ee0133df132c3d973b67b9bd30f9 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 10:37:48 +0000 Subject: [PATCH 29/44] use more pathlib --- .../plugin.py | 20 ++++++++++--------- .../util.py | 3 ++- tests/test_builds.py | 15 +++++++------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 6c90a44..5062025 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -10,6 +10,7 @@ import os import time import multiprocessing +from pathlib import Path from mkdocs import __version__ as mkdocs_version from mkdocs.config import config_options @@ -28,7 +29,7 @@ from packaging.version import Version -HERE = os.path.dirname(os.path.abspath(__file__)) +HERE = Path(__file__).parent.absolute() class GitRevisionDateLocalizedPlugin(BasePlugin): @@ -147,12 +148,13 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: def parallel_compute_commit_timestamps(self, files, original_source: Optional[Dict] = None, is_first_commit=False): pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count())) results = [] - for file in files: - if file.is_documentation_page(): - abs_src_path = file.abs_src_path + for f in files: + if f.is_documentation_page(): + abs_src_path = f.abs_src_path # Support plugins like monorep 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] + abs_src_path = Path(abs_src_path).absolute() result = pool.apply_async(self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit)) results.append((abs_src_path, result)) pool.close() @@ -374,8 +376,8 @@ def on_post_build(self, config: Dict[str, Any], **kwargs) -> None: "js/timeago_mkdocs_material.js", "css/timeago.css", ] - for file in files: - dest_file_path = os.path.join(config["site_dir"], file) - src_file_path = os.path.join(HERE, file) - assert os.path.exists(src_file_path) - copy_file(src_file_path, dest_file_path) + for f in files: + dest_file_path = Path(config["site_dir"]) / f + src_file_path = HERE / f + assert src_file_path.exists() + copy_file(str(src_file_path), str(dest_file_path)) diff --git a/src/mkdocs_git_revision_date_localized_plugin/util.py b/src/mkdocs_git_revision_date_localized_plugin/util.py index 43dbedb..5c43cca 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/util.py +++ b/src/mkdocs_git_revision_date_localized_plugin/util.py @@ -3,6 +3,7 @@ import logging import os import time +from pathlib import Path from mkdocs_git_revision_date_localized_plugin.ci import raise_ci_warnings from mkdocs_git_revision_date_localized_plugin.dates import get_date_formats @@ -34,7 +35,7 @@ def __init__(self, config: Dict, mkdocs_dir: str): self.repo_cache = {} ignore_commits_file = self.config.get("ignored_commits_file") - ignore_commits_filepath = os.path.join(mkdocs_dir, ignore_commits_file) if ignore_commits_file else None + ignore_commits_filepath = Path(mkdocs_dir) / ignore_commits_file if ignore_commits_file else None self.ignored_commits = self.parse_git_ignore_revs(ignore_commits_filepath) if ignore_commits_file else [] def _get_repo(self, path: str) -> Git: diff --git a/tests/test_builds.py b/tests/test_builds.py index ca3e16a..e1a4ef3 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -144,8 +144,7 @@ def setup_commit_history(testproject_path): Returns: repo (repo): git.Repo object """ - assert not os.path.exists(str(testproject_path / ".git")) - testproject_path = str(testproject_path) + assert not (testproject_path / ".git").exists() repo = git.Repo.init(testproject_path, bare=False) repo.git.checkout("-b", "master") @@ -159,7 +158,7 @@ def setup_commit_history(testproject_path): message="add homepage", author=author, date="1500854705" ) # Mon Jul 24 2017 00:05:05 GMT+0000 - file_name = os.path.join(testproject_path, "docs/page_with_tag.md") + file_name = testproject_path / "docs/page_with_tag.md" with open(file_name, "a") as the_file: the_file.write("test\n") repo.git.add("docs/page_with_tag.md") @@ -175,10 +174,10 @@ def setup_commit_history(testproject_path): ) # Sun Jan 23 2022 04:10:26 GMT+0000 if os.path.exists("docs/page_with_renamed.md"): - bf_file_name = os.path.join(testproject_path, "docs/page_with_renamed.md") - af_file_name = os.path.join(testproject_path, "docs/subfolder/page_with_renamed.md") + bf_file_name = testproject_path / "docs/page_with_renamed.md" + af_file_name = testproject_path / "docs/subfolder/page_with_renamed.md" # Since git.mv would actually remove the file, move page_with_renamed.md back to docs if it has been moved - if os.path.exists(af_file_name): + if af_file_name.exists(): os.replace(af_file_name, bf_file_name) repo.git.add("docs/page_with_renamed.md") repo.git.commit( @@ -192,7 +191,7 @@ def setup_commit_history(testproject_path): if os.path.exists("docs/first_page.md"): repo.git.add("docs/first_page.md") repo.git.commit(message="first page", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000 - file_name = os.path.join(testproject_path, "docs/first_page.md") + file_name = testproject_path / "docs/first_page.md" with open(file_name, "w+") as the_file: the_file.write("Hello\n") repo.git.add("docs/first_page.md") @@ -211,7 +210,7 @@ def setup_commit_history(testproject_path): message="add mkdocs", author=author, date="1500854705 -0700" ) # Mon Jul 24 2017 00:05:05 GMT+0000 - if os.path.exists("docs/second_page.md"): + if Path("docs/second_page.md").exists(): repo.git.add("docs/second_page.md") repo.git.commit( message="second page", author=author, date="1643911026" From 2cf7695adc316713b80b3c3cb1e6cfbe7535218d Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 10:48:36 +0000 Subject: [PATCH 30/44] string paths when parallel processing --- src/mkdocs_git_revision_date_localized_plugin/plugin.py | 3 ++- 1 file changed, 2 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 5062025..6141e23 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -155,7 +155,8 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di if original_source and abs_src_path in original_source: abs_src_path = original_source[abs_src_path] abs_src_path = Path(abs_src_path).absolute() - result = pool.apply_async(self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit)) + assert abs_src_path.exists() + result = pool.apply_async(self.util.get_git_commit_timestamp, args=(str(abs_src_path), is_first_commit)) results.append((abs_src_path, result)) pool.close() pool.join() From e21cae8afa4b4722220b86f906382c75423e3cd7 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 10:57:14 +0000 Subject: [PATCH 31/44] ensure string keys --- .../plugin.py | 10 +++++----- tests/fixtures/monorepo/.gitignore | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/fixtures/monorepo/.gitignore diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 6141e23..3d99cac 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -154,9 +154,9 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di # Support plugins like monorep 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] - abs_src_path = Path(abs_src_path).absolute() - assert abs_src_path.exists() - result = pool.apply_async(self.util.get_git_commit_timestamp, args=(str(abs_src_path), is_first_commit)) + 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)) results.append((abs_src_path, result)) pool.close() pool.join() @@ -240,7 +240,7 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con last_revision_hash, last_revision_timestamp = "", int(time.time()) else: last_revision_hash, last_revision_timestamp = self.last_revision_commits.get( - page.file.abs_src_path, (None, None) + 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( @@ -316,7 +316,7 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con first_revision_hash, first_revision_timestamp = "", int(time.time()) else: first_revision_hash, first_revision_timestamp = self.created_commits.get( - page.file.abs_src_path, (None, None) + 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( diff --git a/tests/fixtures/monorepo/.gitignore b/tests/fixtures/monorepo/.gitignore new file mode 100644 index 0000000..0c98940 --- /dev/null +++ b/tests/fixtures/monorepo/.gitignore @@ -0,0 +1,2 @@ +site/ +build/ \ No newline at end of file From b5b485634996a1a7c8c460251e8d761f8025db13 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 11:01:15 +0000 Subject: [PATCH 32/44] Better error handling when parallel proc fails --- .../plugin.py | 12 ++++++++---- tests/fixtures/basic_project/mkdocs_no_parallel.yml | 7 +++++++ tests/test_builds.py | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/basic_project/mkdocs_no_parallel.yml diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 3d99cac..495251b 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -180,10 +180,14 @@ def on_files(self, files: Files, config: MkDocsConfig): else: original_source = None - if not self.last_revision_commits: - self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=False) - if not self.created_commits: - self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=True) + try: + if not self.last_revision_commits: + self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=False) + if not self.created_commits: + self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=True) + except Exception as e: + logging.warning(f"Parallel processing failed: {str(e)}.\n To fall back to serial processing, use 'enable_parallel_processing: False' setting.") + def on_page_markdown(self, markdown: str, page: Page, config: config_options.Config, files, **kwargs) -> str: """ diff --git a/tests/fixtures/basic_project/mkdocs_no_parallel.yml b/tests/fixtures/basic_project/mkdocs_no_parallel.yml new file mode 100644 index 0000000..8807c99 --- /dev/null +++ b/tests/fixtures/basic_project/mkdocs_no_parallel.yml @@ -0,0 +1,7 @@ +site_name: test gitrevisiondatelocalized_plugin +use_directory_urls: true + +plugins: + - search + - git-revision-date-localized: + enable_parallel_processing: False \ No newline at end of file diff --git a/tests/test_builds.py b/tests/test_builds.py index e1a4ef3..dea8b2f 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -333,6 +333,7 @@ def validate_mkdocs_file(temp_path: str, mkdocs_yml_file: str): "basic_project/mkdocs_fallback_to_build_date.yml", "basic_project/mkdocs_locale.yml", "basic_project/mkdocs_meta.yml", + "basic_project/mkdocs_no_parallel.yml", "basic_project/mkdocs_plugin_locale.yml", "basic_project/mkdocs.yml", "basic_project/mkdocs_theme_timeago_locale.yml", From 26f892109733c73d36ce2359e7cbfad8a453cc2b Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 11:07:34 +0000 Subject: [PATCH 33/44] test with disable --- tests/fixtures/monorepo/mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fixtures/monorepo/mkdocs.yml b/tests/fixtures/monorepo/mkdocs.yml index 3b9798e..aeaa9f3 100644 --- a/tests/fixtures/monorepo/mkdocs.yml +++ b/tests/fixtures/monorepo/mkdocs.yml @@ -14,5 +14,5 @@ theme: plugins: - search - - git-revision-date-localized + # - git-revision-date-localized - monorepo From a5692df321195ff6969babb3753b3f776822366b Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 6 Mar 2025 11:57:01 +0000 Subject: [PATCH 34/44] disable monorepo tests on windows --- tests/fixtures/monorepo/mkdocs.yml | 2 +- .../monorepo/mkdocs_reverse_order.yml | 19 +++++++++++++++++++ tests/test_builds.py | 15 +++++++++------ 3 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 tests/fixtures/monorepo/mkdocs_reverse_order.yml diff --git a/tests/fixtures/monorepo/mkdocs.yml b/tests/fixtures/monorepo/mkdocs.yml index aeaa9f3..3b9798e 100644 --- a/tests/fixtures/monorepo/mkdocs.yml +++ b/tests/fixtures/monorepo/mkdocs.yml @@ -14,5 +14,5 @@ theme: plugins: - search - # - git-revision-date-localized + - git-revision-date-localized - monorepo diff --git a/tests/fixtures/monorepo/mkdocs_reverse_order.yml b/tests/fixtures/monorepo/mkdocs_reverse_order.yml new file mode 100644 index 0000000..c341095 --- /dev/null +++ b/tests/fixtures/monorepo/mkdocs_reverse_order.yml @@ -0,0 +1,19 @@ +site_name: Cats API + +nav: + - Intro: 'index.md' + - Authentication: 'authentication.md' + - Components: '*include ./components/*/mkdocs.yml' + - API: + - v1: '!include ./v1/mkdocs.yml' + - v2: '!include ./v2/mkdocs.yml' + - v3: '!include ./v3/mkdocs.yml' + +theme: + name: material + +plugins: + - search + # Note; here monorepo is defined after git-revision-date-localized + - monorepo + - git-revision-date-localized diff --git a/tests/test_builds.py b/tests/test_builds.py index dea8b2f..a85022e 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -13,6 +13,7 @@ # standard lib import logging import os +import sys import re import shutil from contextlib import contextmanager @@ -680,14 +681,16 @@ def test_ignored_commits(tmp_path): assert "May 4, 2018" in contents +@pytest.mark.skipif(sys.platform.startswith("win"), reason="monorepo plugin did not work for me on windows (even without this plugin)") def test_monorepo_compat(tmp_path): testproject_path = setup_clean_mkdocs_folder("tests/fixtures/monorepo/mkdocs.yml", tmp_path) - repo = setup_commit_history(testproject_path) + setup_commit_history(testproject_path) result = build_docs_setup(testproject_path) + assert result.exit_code == 0, f"'mkdocs build' command failed with:\n\n{result.stdout}" - # author = "Test Person " - # with working_directory(testproject_path): - # repo.git.add(".") - # repo.git.commit(message="add all", author=author, date="1500854705") - +@pytest.mark.skipif(sys.platform.startswith("win"), reason="monorepo plugin did not work for me on windows (even without this plugin)") +def test_monorepo_compat_reverse_order(tmp_path): + testproject_path = setup_clean_mkdocs_folder("tests/fixtures/monorepo/mkdocs_reverse_order.yml", tmp_path) + setup_commit_history(testproject_path) + result = build_docs_setup(testproject_path) assert result.exit_code == 0, f"'mkdocs build' command failed with:\n\n{result.stdout}" From 232fe7ae7d0393fd8cc1c4dc9af974d814de15eb Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Sun, 9 Mar 2025 12:38:07 +0000 Subject: [PATCH 35/44] Fix for generated files when using parallel proc --- .../__init__.py | 2 +- .../plugin.py | 17 ++++++++++++++--- tests/fixtures/basic_project/gen_files.py | 5 +++++ .../basic_project/mkdocs_plugin_genfiles.yml | 9 +++++++++ tests/test_builds.py | 14 ++++++++++++++ 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/basic_project/gen_files.py create mode 100644 tests/fixtures/basic_project/mkdocs_plugin_genfiles.yml diff --git a/src/mkdocs_git_revision_date_localized_plugin/__init__.py b/src/mkdocs_git_revision_date_localized_plugin/__init__.py index bf25615..daa50c7 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/__init__.py +++ b/src/mkdocs_git_revision_date_localized_plugin/__init__.py @@ -1 +1 @@ -__version__ = "1.4.1" +__version__ = "1.4.2" diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 495251b..671b352 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -149,11 +149,21 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count())) results = [] for f in files: - if f.is_documentation_page(): + print(f.abs_src_path) + if not f.is_documentation_page(): + continue + elif getattr(f, "generated_by", None): + continue + elif f.abs_src_path is None: + continue + elif exclude(f.src_path, self.config.get("exclude", [])): + continue + else: abs_src_path = f.abs_src_path - # Support plugins like monorep that might have moved the files from the original source that is under git + # 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] + 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)) @@ -182,11 +192,12 @@ def on_files(self, files: Files, config: MkDocsConfig): try: if not self.last_revision_commits: - self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=False) + self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=False) if not self.created_commits: self.parallel_compute_commit_timestamps(files=files, original_source=original_source, is_first_commit=True) except Exception as e: logging.warning(f"Parallel processing failed: {str(e)}.\n To fall back to serial processing, use 'enable_parallel_processing: False' setting.") + raise e def on_page_markdown(self, markdown: str, page: Page, config: config_options.Config, files, **kwargs) -> str: diff --git a/tests/fixtures/basic_project/gen_files.py b/tests/fixtures/basic_project/gen_files.py new file mode 100644 index 0000000..8534291 --- /dev/null +++ b/tests/fixtures/basic_project/gen_files.py @@ -0,0 +1,5 @@ +# for testing with generated files. See mkdocs_plugin_genfiles.yml +import mkdocs_gen_files + +with mkdocs_gen_files.open("foo.md", "w") as f: + print("Bar, world!", file=f) \ No newline at end of file diff --git a/tests/fixtures/basic_project/mkdocs_plugin_genfiles.yml b/tests/fixtures/basic_project/mkdocs_plugin_genfiles.yml new file mode 100644 index 0000000..abbf9f8 --- /dev/null +++ b/tests/fixtures/basic_project/mkdocs_plugin_genfiles.yml @@ -0,0 +1,9 @@ +site_name: test gitrevisiondatelocalized_plugin +use_directory_urls: true + +plugins: + - search + - gen-files: + scripts: + - gen_files.py # or any other name or path + - git-revision-date-localized \ No newline at end of file diff --git a/tests/test_builds.py b/tests/test_builds.py index a85022e..417afd6 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -119,8 +119,10 @@ def setup_clean_mkdocs_folder(mkdocs_yml_path, output_path): else: shutil.copytree("tests/fixtures/basic_project/docs", str(testproject_path / "docs")) + shutil.copyfile("tests/fixtures/basic_project/gen_files.py", str(testproject_path / "gen_files.py")) shutil.copyfile(mkdocs_yml_path, str(testproject_path / "mkdocs.yml")) + if "gen-files" in mkdocs_yml_path: shutil.copyfile(str(Path(mkdocs_yml_path).parent / "gen_pages.py"), str(testproject_path / "gen_pages.py")) @@ -694,3 +696,15 @@ def test_monorepo_compat_reverse_order(tmp_path): setup_commit_history(testproject_path) result = build_docs_setup(testproject_path) assert result.exit_code == 0, f"'mkdocs build' command failed with:\n\n{result.stdout}" + + +def test_genfiles_plugin(tmp_path): + testproject_path = setup_clean_mkdocs_folder("tests/fixtures/basic_project/mkdocs_plugin_genfiles.yml", tmp_path) + setup_commit_history(testproject_path) + + result = build_docs_setup(testproject_path) + assert result.exit_code == 0, f"'mkdocs build' command failed with:\n\n{result.stdout}" + + page_with_tag = testproject_path / "site/foo/index.html" + contents = page_with_tag.read_text(encoding="utf8") + assert "Bar, world!" in contents \ No newline at end of file From ce3849409bd2f273271aa5c56ceed2c5a41d9aa3 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Sun, 9 Mar 2025 12:49:51 +0000 Subject: [PATCH 36/44] remove print() --- src/mkdocs_git_revision_date_localized_plugin/plugin.py | 1 - 1 file changed, 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 671b352..79c60e5 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -149,7 +149,6 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count())) results = [] for f in files: - print(f.abs_src_path) if not f.is_documentation_page(): continue elif getattr(f, "generated_by", None): From bd876e5c6c99ab92f515d479fd5655a77933b4e9 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Mon, 10 Mar 2025 08:07:35 +0000 Subject: [PATCH 37/44] bump version --- src/mkdocs_git_revision_date_localized_plugin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/__init__.py b/src/mkdocs_git_revision_date_localized_plugin/__init__.py index daa50c7..7802f2f 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/__init__.py +++ b/src/mkdocs_git_revision_date_localized_plugin/__init__.py @@ -1 +1 @@ -__version__ = "1.4.2" +__version__ = "1.4.4" \ No newline at end of file From 1befc7bf383a13deca41bbe6ebbab34cd1ca4d2d Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Thu, 13 Mar 2025 09:09:15 +0100 Subject: [PATCH 38/44] Update unittests.yml typo --- .github/workflows/unittests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 3703886..0eacc2d 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -2,7 +2,7 @@ name: Unit tests on: [pull_request] jobs: run: - name: Run unit tests with codecov upload + name: Run unit tests runs-on: ${{ matrix.os }} env: USING_COVERAGE: '3.10' @@ -23,4 +23,4 @@ jobs: git config --global user.name "Github Action" git config --global user.email "githubaction@gmail.com" uv run pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report=xml - \ No newline at end of file + From 95e8d78bcfba31fa09281a1e84ea6a4d8f04624e Mon Sep 17 00:00:00 2001 From: Timo Reymann Date: Fri, 14 Mar 2025 12:06:41 +0100 Subject: [PATCH 39/44] Add more robust check for monorepo plugin configuration --- src/mkdocs_git_revision_date_localized_plugin/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/plugin.py b/src/mkdocs_git_revision_date_localized_plugin/plugin.py index 79c60e5..d9d74bc 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/src/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -184,8 +184,9 @@ def on_files(self, files: Files, config: MkDocsConfig): return # Support monorepo/techdocs, which copies the docs_dir to a temporary directory - if "monorepo" in config.get("plugins", {}): - original_source = config.get("plugins").get("monorepo").merger.files_source_dir + 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: + original_source = mono_repo_plugin.merger.files_source_dir else: original_source = None From d6515248dc949d629000d1525db0d97ed20cf106 Mon Sep 17 00:00:00 2001 From: Timo Reymann Date: Fri, 14 Mar 2025 12:06:58 +0100 Subject: [PATCH 40/44] Add dev dependency for testing techdocs-core integration --- pyproject.toml | 1 + uv.lock | 66 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d44d1c3..1fb6605 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,6 +119,7 @@ dev-dependencies = [ "mkdocs-gen-files>=0.5.0", "mkdocs-git-authors-plugin>=0.9.2", "mkdocs-material>=9.6.7", + "mkdocs-techdocs-core", "mkdocs-monorepo-plugin>=1.1.0", "mkdocs-static-i18n>=1.3.0", "pytest>=8.3.5", diff --git a/uv.lock b/uv.lock index a4ce7b3..7e48db8 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,4 @@ version = 1 -revision = 1 requires-python = ">=3.8" resolution-markers = [ "python_full_version >= '3.9'", @@ -441,15 +440,15 @@ wheels = [ [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe", version = "2.1.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "markupsafe", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] @@ -676,6 +675,7 @@ wheels = [ [[package]] name = "mkdocs-git-revision-date-localized-plugin" +version = "1.4.4" source = { editable = "." } dependencies = [ { name = "babel" }, @@ -693,6 +693,7 @@ dev = [ { name = "mkdocs-material" }, { name = "mkdocs-monorepo-plugin" }, { name = "mkdocs-static-i18n" }, + { name = "mkdocs-techdocs-core" }, { name = "pytest" }, { name = "pytest-cov", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "pytest-cov", version = "6.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, @@ -716,6 +717,7 @@ dev = [ { name = "mkdocs-material", specifier = ">=9.6.7" }, { name = "mkdocs-monorepo-plugin", specifier = ">=1.1.0" }, { name = "mkdocs-static-i18n", specifier = ">=1.3.0" }, + { name = "mkdocs-techdocs-core" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "pytest-cov", specifier = ">=5.0.0" }, { name = "ruff" }, @@ -723,7 +725,7 @@ dev = [ [[package]] name = "mkdocs-material" -version = "9.6.7" +version = "9.6.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "babel" }, @@ -739,9 +741,9 @@ dependencies = [ { name = "pymdown-extensions" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/d7/93e19c9587e5f4ed25647890555d58cf484a4d412be7037dc17b9c9179d9/mkdocs_material-9.6.7.tar.gz", hash = "sha256:3e2c1fceb9410056c2d91f334a00cdea3215c28750e00c691c1e46b2a33309b4", size = 3947458 } +sdist = { url = "https://files.pythonhosted.org/packages/10/0a/17557708cfc6a11a1a941199b6b54a8990b297d910db81a43f1082b11e1b/mkdocs_material-9.6.8.tar.gz", hash = "sha256:8de31bb7566379802532b248bd56d9c4bc834afc4625884bf5769f9412c6a354", size = 3948078 } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/d3/12f22de41bdd9e576ddc459b38c651d68edfb840b32acaa1f46ae36845e3/mkdocs_material-9.6.7-py3-none-any.whl", hash = "sha256:8a159e45e80fcaadd9fbeef62cbf928569b93df954d4dc5ba76d46820caf7b47", size = 8696755 }, + { url = "https://files.pythonhosted.org/packages/18/fd/0e6aa44f5b4fb5a386f19c398222a6c75a313d52567ba992bad691cf0d80/mkdocs_material-9.6.8-py3-none-any.whl", hash = "sha256:0a51532dd8aa80b232546c073fe3ef60dfaef1b1b12196ac7191ee01702d1cf8", size = 8697857 }, ] [[package]] @@ -778,6 +780,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ca/f7/ef222a7a2f96ecf79c7c00bfc9dde3b22cd2cc1bd2b7472c7b204fc64225/mkdocs_static_i18n-1.3.0-py3-none-any.whl", hash = "sha256:7905d52fff71d2c108b6c344fd223e848ca7e39ddf319b70864dfa47dba85d6b", size = 21660 }, ] +[[package]] +name = "mkdocs-techdocs-core" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/03/e7fbf44586064a33fbe4a0ea868bd6d482688baeb6fa06a5dfa44ca17312/mkdocs-techdocs-core-0.0.3.tar.gz", hash = "sha256:b45bf3c240e8332d29ea275194cb4f4b26d22e1df505df4af219eeb3cc9bbafd", size = 3072 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/d7/7c3a9b6c216e06a751ed70440818b12e5e78fbfb87f02b147cfc21f12e0b/mkdocs_techdocs_core-0.0.3-py3-none-any.whl", hash = "sha256:56c35a852093a3422671cdeeb1eb169ed8e51348b5956e54b269815971fab9ce", size = 3058 }, +] + [[package]] name = "packaging" version = "24.2" @@ -1017,27 +1031,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.9" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/418441a8170e8d53d05c0b9dad69760dbc7b8a12c10dbe6db1e1205d2377/ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933", size = 3717448 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ec/9c59d2956566517c98ac8267554f4eaceafb2a19710a429368518b7fab43/ruff-0.10.0.tar.gz", hash = "sha256:fa1554e18deaf8aa097dbcfeafaf38b17a2a1e98fdc18f50e62e8a836abee392", size = 3789921 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/c3/2c4afa9ba467555d074b146d9aed0633a56ccdb900839fb008295d037b89/ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367", size = 10027252 }, - { url = "https://files.pythonhosted.org/packages/33/d1/439e58487cf9eac26378332e25e7d5ade4b800ce1eec7dc2cfc9b0d7ca96/ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7", size = 10840721 }, - { url = "https://files.pythonhosted.org/packages/50/44/fead822c38281ba0122f1b76b460488a175a9bd48b130650a6fb6dbcbcf9/ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d", size = 10161439 }, - { url = "https://files.pythonhosted.org/packages/11/ae/d404a2ab8e61ddf6342e09cc6b7f7846cce6b243e45c2007dbe0ca928a5d/ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a", size = 10336264 }, - { url = "https://files.pythonhosted.org/packages/6a/4e/7c268aa7d84cd709fb6f046b8972313142cffb40dfff1d2515c5e6288d54/ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe", size = 9908774 }, - { url = "https://files.pythonhosted.org/packages/cc/26/c618a878367ef1b76270fd027ca93692657d3f6122b84ba48911ef5f2edc/ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c", size = 11428127 }, - { url = "https://files.pythonhosted.org/packages/d7/9a/c5588a93d9bfed29f565baf193fe802fa676a0c837938137ea6cf0576d8c/ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be", size = 12133187 }, - { url = "https://files.pythonhosted.org/packages/3e/ff/e7980a7704a60905ed7e156a8d73f604c846d9bd87deda9cabfa6cba073a/ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590", size = 11602937 }, - { url = "https://files.pythonhosted.org/packages/24/78/3690444ad9e3cab5c11abe56554c35f005b51d1d118b429765249095269f/ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb", size = 13771698 }, - { url = "https://files.pythonhosted.org/packages/6e/bf/e477c2faf86abe3988e0b5fd22a7f3520e820b2ee335131aca2e16120038/ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0", size = 11249026 }, - { url = "https://files.pythonhosted.org/packages/f7/82/cdaffd59e5a8cb5b14c408c73d7a555a577cf6645faaf83e52fe99521715/ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17", size = 10220432 }, - { url = "https://files.pythonhosted.org/packages/fe/a4/2507d0026225efa5d4412b6e294dfe54725a78652a5c7e29e6bd0fc492f3/ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1", size = 9874602 }, - { url = "https://files.pythonhosted.org/packages/d5/be/f3aab1813846b476c4bcffe052d232244979c3cd99d751c17afb530ca8e4/ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57", size = 10851212 }, - { url = "https://files.pythonhosted.org/packages/8b/45/8e5fd559bea0d2f57c4e12bf197a2fade2fac465aa518284f157dfbca92b/ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e", size = 11327490 }, - { url = "https://files.pythonhosted.org/packages/42/55/e6c90f13880aeef327746052907e7e930681f26a164fe130ddac28b08269/ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1", size = 10227912 }, - { url = "https://files.pythonhosted.org/packages/35/b2/da925693cb82a1208aa34966c0f36cb222baca94e729dd22a587bc22d0f3/ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1", size = 11355632 }, - { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 }, + { url = "https://files.pythonhosted.org/packages/bf/3f/742afe91b43def2a75990b293c676355576c0ff9cdbcf4249f78fa592544/ruff-0.10.0-py3-none-linux_armv6l.whl", hash = "sha256:46a2aa0eaae5048e5f804f0be9489d8a661633e23277b7293089e70d5c1a35c4", size = 10078369 }, + { url = "https://files.pythonhosted.org/packages/8d/a0/8696fb4862e82f7b40bbbc2917137594b22826cc62d77278a91391507514/ruff-0.10.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:775a6bc61af9dd0a2e1763406522a137e62aabb743d8b43ed95f019cdd1526c7", size = 10876912 }, + { url = "https://files.pythonhosted.org/packages/40/aa/0d48b7b7d7a1f168bb8fd893ed559d633c7d68c4a8ef9b996f0c2bd07aca/ruff-0.10.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8b03e6fcd39d20f0004f9956f0ed5eadc404d3a299f9d9286323884e3b663730", size = 10229962 }, + { url = "https://files.pythonhosted.org/packages/21/de/861ced2f75b045d8cfc038d68961d8ac117344df1f43a11abdd05bf7991b/ruff-0.10.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:621101d1af80248827f2409a78c8177c8319986a57b4663613b9c72f8617bfcd", size = 10404627 }, + { url = "https://files.pythonhosted.org/packages/21/69/666e0b840191c3ce433962c0d05fc0f6800afe259ea5d230cc731655d8e2/ruff-0.10.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2dfe85cb6bfbd4259801e7d4982f2a72bdbd5749dc73a09d68a6dbf77f2209a", size = 9939383 }, + { url = "https://files.pythonhosted.org/packages/76/bf/34a2adc58092c99cdfa9f1303acd82d840d56412022e477e2ab20c261d2d/ruff-0.10.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43ac3879a20c22fdc57e559f0bb27f0c71828656841d0b42d3505b1e5b3a83c8", size = 11492269 }, + { url = "https://files.pythonhosted.org/packages/31/3d/f7ccfcf69f15948623b190feea9d411d5029ae39725fcc078f8d43bd07a6/ruff-0.10.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ef5e3aac421bbc62f8a7aab21edd49a359ed42205f7a5091a74386bca1efa293", size = 12186939 }, + { url = "https://files.pythonhosted.org/packages/6e/3e/c557c0abfdea85c7d238a3cb238c73e7b6d17c30a584234c4fd8fe2cafb6/ruff-0.10.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f4f62d7fac8b748fce67ad308116b4d4cc1a9f964b4804fc5408fbd06e13ba9", size = 11655896 }, + { url = "https://files.pythonhosted.org/packages/3b/8e/3bfa110f37e5192eb3943f14943d05fbb9a76fea380aa87655e6f6276a54/ruff-0.10.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f9f6205c5b0d626f98da01a0e75b724a64c21c554bba24b12522c9e9ba6a04", size = 13885502 }, + { url = "https://files.pythonhosted.org/packages/51/4a/22cdab59b5563dd7f4c504d0f1e6bb25fc800a5a057395bc24f8ff3a85b2/ruff-0.10.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46a97f3d55f68464c48d1e929a8582c7e5bb80ac73336bbc7b0da894d8e6cd9e", size = 11344767 }, + { url = "https://files.pythonhosted.org/packages/3d/0f/8f85de2ac565f82f47c6d8fb7ae04383e6300560f2d1b91c1268ff91e507/ruff-0.10.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0b811197d0dc96c13d610f8cfdc56030b405bcff5c2f10eab187b329da0ca4a", size = 10300331 }, + { url = "https://files.pythonhosted.org/packages/90/4a/b337df327832cb30bd8607e8d1fdf1b6b5ca228307d5008dd49028fb66ae/ruff-0.10.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a13a3fda0870c1c964b47ff5d73805ae80d2a9de93ee2d185d453b8fddf85a84", size = 9926551 }, + { url = "https://files.pythonhosted.org/packages/d7/e9/141233730b85675ac806c4b62f70516bd9c0aae8a55823f3a6589ed411be/ruff-0.10.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6ceb8d9f062e90ddcbad929f6136edf764bbf6411420a07e8357602ea28cd99f", size = 10925061 }, + { url = "https://files.pythonhosted.org/packages/24/09/02987935b55c2d353a226ac1b4f9718830e2e195834929f46c07eeede746/ruff-0.10.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c41d07d573617ed2f287ea892af2446fd8a8d877481e8e1ba6928e020665d240", size = 11394949 }, + { url = "https://files.pythonhosted.org/packages/d6/ec/054f9879fb6f4122d43ffe5c9f88c8c323a9cd14220d5c813aea5805e02c/ruff-0.10.0-py3-none-win32.whl", hash = "sha256:76e2de0cbdd587e373cd3b4050d2c45babdd7014c1888a6f121c29525c748a15", size = 10272077 }, + { url = "https://files.pythonhosted.org/packages/6e/49/915d8682f24645b904fe6a1aac36101464fc814923fdf293c1388dc5533c/ruff-0.10.0-py3-none-win_amd64.whl", hash = "sha256:f943acdecdcc6786a8d1dad455dd9f94e6d57ccc115be4993f9b52ef8316027a", size = 11393300 }, + { url = "https://files.pythonhosted.org/packages/82/ed/5c59941634c9026ceeccc7c119f23f4356f09aafd28c15c1bc734ac66b01/ruff-0.10.0-py3-none-win_arm64.whl", hash = "sha256:935a943bdbd9ff0685acd80d484ea91088e27617537b5f7ef8907187d19d28d0", size = 10510133 }, ] [[package]] From d114ccea1feb3b5692abd1b48473ad39395f2f72 Mon Sep 17 00:00:00 2001 From: Timo Reymann Date: Fri, 14 Mar 2025 12:12:53 +0100 Subject: [PATCH 41/44] Test build works for techdocs-core plugin --- tests/fixtures/techdocs-core/docs/authentication.md | 0 tests/fixtures/techdocs-core/docs/index.md | 9 +++++++++ tests/fixtures/techdocs-core/mkdocs.yml | 13 +++++++++++++ tests/test_builds.py | 1 + 4 files changed, 23 insertions(+) create mode 100644 tests/fixtures/techdocs-core/docs/authentication.md create mode 100644 tests/fixtures/techdocs-core/docs/index.md create mode 100644 tests/fixtures/techdocs-core/mkdocs.yml diff --git a/tests/fixtures/techdocs-core/docs/authentication.md b/tests/fixtures/techdocs-core/docs/authentication.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/techdocs-core/docs/index.md b/tests/fixtures/techdocs-core/docs/index.md new file mode 100644 index 0000000..b33485e --- /dev/null +++ b/tests/fixtures/techdocs-core/docs/index.md @@ -0,0 +1,9 @@ +# Welcome to our Developer Platform + +This contains the documentation for our Developer Platform. + +## API + + +- Markdown source: `sample-docs/docs/index.md` +- Permalink: diff --git a/tests/fixtures/techdocs-core/mkdocs.yml b/tests/fixtures/techdocs-core/mkdocs.yml new file mode 100644 index 0000000..df73b72 --- /dev/null +++ b/tests/fixtures/techdocs-core/mkdocs.yml @@ -0,0 +1,13 @@ +site_name: Cats API + +nav: + - Intro: 'index.md' + - Authentication: 'authentication.md' + +theme: + name: material + +plugins: + - techdocs-core + - git-revision-date-localized: + enable_creation_date: true diff --git a/tests/test_builds.py b/tests/test_builds.py index 417afd6..5dfc57e 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -351,6 +351,7 @@ def validate_mkdocs_file(temp_path: str, mkdocs_yml_file: str): "basic_project/mkdocs_timeago_locale.yml", "basic_project/mkdocs_timeago.yml", "basic_project/mkdocs_with_override.yml", + "techdocs-core/mkdocs.yml", # 'i18n/mkdocs.yml' ] From b092d8789351e9c1fe4ce5967f222829b78b79fb Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Fri, 14 Mar 2025 12:44:10 +0000 Subject: [PATCH 42/44] bump to 1.4.5 --- src/mkdocs_git_revision_date_localized_plugin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mkdocs_git_revision_date_localized_plugin/__init__.py b/src/mkdocs_git_revision_date_localized_plugin/__init__.py index 7802f2f..41affe5 100644 --- a/src/mkdocs_git_revision_date_localized_plugin/__init__.py +++ b/src/mkdocs_git_revision_date_localized_plugin/__init__.py @@ -1 +1 @@ -__version__ = "1.4.4" \ No newline at end of file +__version__ = "1.4.5" \ No newline at end of file From aab7303e972f0038d02c70ca8aa528ab868456e5 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Fri, 14 Mar 2025 12:44:21 +0000 Subject: [PATCH 43/44] add python version file --- .gitignore | 2 -- .python-version | 1 + uv.lock | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 .python-version diff --git a/.gitignore b/.gitignore index 3f960da..7b91b20 100644 --- a/.gitignore +++ b/.gitignore @@ -157,8 +157,6 @@ coverage.xml docs/_build/ # PyBuilder target/ -# pyenv -.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/uv.lock b/uv.lock index 7e48db8..951682f 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.8" resolution-markers = [ "python_full_version >= '3.9'", @@ -675,7 +676,6 @@ wheels = [ [[package]] name = "mkdocs-git-revision-date-localized-plugin" -version = "1.4.4" source = { editable = "." } dependencies = [ { name = "babel" }, From 2fd812fd567c90a12ea9400665c8f62faf40366e Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Fri, 14 Mar 2025 12:47:11 +0000 Subject: [PATCH 44/44] skip monorepo on windows --- tests/test_builds.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_builds.py b/tests/test_builds.py index 5dfc57e..8d70149 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -371,6 +371,8 @@ def test_tags_are_replaced(tmp_path, mkdocs_file): """ Make sure the {{ }} tags are replaced properly. """ + if sys.platform.startswith("win") and ("monorepo" in mkdocs_file or "techdocs" in mkdocs_file): + pytest.skip("monorepo plugin windows issue (even without this plugin)") testproject_path = setup_clean_mkdocs_folder(mkdocs_yml_path=f"tests/fixtures/{mkdocs_file}", output_path=tmp_path) setup_commit_history(testproject_path) result = build_docs_setup(testproject_path)