diff --git a/mkdocs.yml b/mkdocs.yml index 5a1796a..6a516d7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -39,7 +39,8 @@ markdown_extensions: pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets: - base_path: "." + base_path: + - "." check_paths: true - pymdownx.superfences - pymdownx.tabbed: @@ -105,7 +106,6 @@ plugins: abstract_delimiter: date_from_meta: as_creation: "date" - as_update: false datetime_format: "%Y-%m-%d %H:%M" default_timezone: "Europe/Paris" default_time: "22:00" diff --git a/mkdocs_rss_plugin/config.py b/mkdocs_rss_plugin/config.py index e335d58..49d7482 100644 --- a/mkdocs_rss_plugin/config.py +++ b/mkdocs_rss_plugin/config.py @@ -4,6 +4,9 @@ # ########## Libraries ############# # ################################## +# standard +from datetime import datetime +from typing import Union # 3rd party from mkdocs.config import config_options @@ -14,6 +17,16 @@ # ################################## +class _DateFromMeta(Config): + # TODO: remove deprecated code in future version. Only str values will be accepted + # for as_creation and as_update + as_creation = config_options.Type(Union[bool, str], default="git") + as_update = config_options.Type(Union[bool, str], default="git") + datetime_format = config_options.Type(str, default="%Y-%m-%d %H:%M") + default_time = config_options.Type(str, default=datetime.min.strftime("%H:%M")) + default_timezone = config_options.Type(str, default="UTC") + + class _FeedsFilenamesConfig(Config): json_created = config_options.Type(str, default="feed_json_created.json") json_updated = config_options.Type(str, default="feed_json_updated.json") @@ -30,7 +43,7 @@ class RssPluginConfig(Config): config_options.ListOfItems(config_options.Type(str)) ) comments_path = config_options.Optional(config_options.Type(str)) - date_from_meta = config_options.Optional(config_options.Type(dict)) + date_from_meta = config_options.SubConfig(_DateFromMeta) enabled = config_options.Type(bool, default=True) feed_ttl = config_options.Type(int, default=1440) feeds_filenames = config_options.SubConfig(_FeedsFilenamesConfig) diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index 4e8f18b..0d5f873 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -10,13 +10,15 @@ from datetime import datetime from email.utils import formatdate from pathlib import Path -from re import compile +from re import compile as re_compile # 3rd party from jinja2 import Environment, FileSystemLoader, select_autoescape from mkdocs.config import config_options +from mkdocs.config.defaults import MkDocsConfig from mkdocs.exceptions import PluginError from mkdocs.plugins import BasePlugin, event_priority, get_plugin_logger +from mkdocs.structure.files import Files from mkdocs.structure.pages import Page from mkdocs.utils import get_build_timestamp @@ -55,30 +57,28 @@ class GitRssPlugin(BasePlugin[RssPluginConfig]): def __init__(self): """Instanciation.""" - # dates source - self.src_date_created = self.src_date_updated = "git" - self.meta_datetime_format: str | None = None - self.meta_default_timezone: str = "UTC" - self.meta_default_time: datetime.time | None = None # pages storage self.pages_to_filter: list = [] # prepare output feeds self.feed_created: dict = {} self.feed_updated: dict = {} - def on_config(self, config: config_options.Config) -> dict: + def on_config(self, config: MkDocsConfig) -> MkDocsConfig: """The config event is the first event called on build and is run immediately after the user configuration is loaded and validated. Any alterations to the config should be made here. - https://www.mkdocs.org/user-guide/plugins/#on_config - :param config: global configuration object - :type config: config_options.Config + See: https://www.mkdocs.org/user-guide/plugins/#on_config - :raises FileExistsError: if the template for the RSS feed is not found + Args: + config (config_options.Config): global configuration object - :return: plugin configuration object - :rtype: dict + Raises: + FileExistsError: if the template for the RSS feed is not found + PluginError: if the 'date_from_meta.default_time' format does not comply + + Returns: + MkDocsConfig: global configuration object """ # Skip if disabled if not self.config.enabled: @@ -131,48 +131,56 @@ def on_config(self, config: config_options.Config) -> dict: base_feed["logo_url"] = self.config.image # pattern to match pages included in output - self.match_path_pattern = compile(self.config.match_path) + self.match_path_pattern = re_compile(self.config.match_path) # date handling - if self.config.date_from_meta is not None: - self.src_date_created = self.config.date_from_meta.get("as_creation", False) - self.src_date_updated = self.config.date_from_meta.get("as_update", False) - self.meta_datetime_format = self.config.date_from_meta.get( - "datetime_format", "%Y-%m-%d %H:%M" + if ( + self.config.date_from_meta.as_creation == "git" + and self.config.date_from_meta.as_update == "git" + ): + logger.debug("Dates will be retrieved from git log.") + elif any( + [ + isinstance(self.config.date_from_meta.as_creation, bool), + isinstance(self.config.date_from_meta.as_update, bool), + ] + ): + deprecation_msg = ( + "Since version 1.13, using a boolean for " + "'date_from_meta.as_creation' and 'date_from_meta.as_update' is " + "deprecated. Please update your " + "`rss` plugin settings in your Mkdocs configuration " + f"({config.config_file_path}) by using a str or removing the value if " + "you were using `False`., " ) - self.meta_default_timezone = self.config.date_from_meta.get( - "default_timezone", "UTC" + logger.warning(DeprecationWarning(deprecation_msg)) + self.config.date_from_meta.as_creation = ( + self.config.date_from_meta.as_update + ) = "git" + + # check if default time complies with expected format + try: + self.config.date_from_meta.default_time = datetime.strptime( + self.config.date_from_meta.default_time, "%H:%M" ) - self.meta_default_time = self.config.date_from_meta.get( - "default_time", None + except ValueError as err: + raise PluginError( + "Config error: `date_from_meta.default_time` value " + f"'{self.config.date_from_meta.default_time}' format doesn't match the " + f"expected format %H:%M. Trace: {err}" ) - if self.meta_default_time: - try: - self.meta_default_time = datetime.strptime( - self.meta_default_time, "%H:%M" - ) - except ValueError as err: - raise PluginError( - "Config error: `date_from_meta.default_time` value " - f"'{self.meta_default_time}' format doesn't match the expected " - f"format %H:%M. Trace: {err}" - ) - else: - self.meta_default_time = datetime.min - if self.config.use_git: - logger.debug( - "Dates will be retrieved FIRSTLY from page meta (yaml " - "frontmatter). The git log will be used as fallback." - ) - else: - logger.debug( - "Dates will be retrieved ONLY from page meta (yaml " - "frontmatter). The build date will be used as fallback, without any " - "call to Git." - ) + if self.config.use_git: + logger.debug( + "Dates will be retrieved FIRSTLY from page meta (yaml " + "frontmatter). The git log will be used as fallback." + ) else: - logger.debug("Dates will be retrieved from git log.") + logger.debug( + "Dates will be retrieved ONLY from page meta (yaml " + "frontmatter). The build date will be used as fallback, without any " + "call to Git." + ) # create 2 final dicts self.feed_created = deepcopy(base_feed) @@ -199,34 +207,32 @@ def on_config(self, config: config_options.Config) -> dict: "configuration file whereas a URL is mandatory to publish. " "See: https://validator.w3.org/feed/docs/rss2.html#requiredChannelElements" ) - self.feed_created["rss_url"] = self.feed_updated["rss_url"] = None + self.feed_created["rss_url"] = self.feed_updated["json_url"] = ( + self.feed_updated["rss_url"] + ) = self.feed_updated["json_url"] = None # ending event return config @event_priority(priority=-75) def on_page_content( - self, html: str, page: Page, config: config_options.Config, files + self, html: str, page: Page, config: MkDocsConfig, files: Files ) -> str | None: - """The page_content event is called after the Markdown text is rendered - to HTML (but before being passed to a template) and can be used to alter - the HTML body of the page. + """The page_content event is called after the Markdown text is rendered to HTML + (but before being passed to a template) and can be used to alter the HTML + body of the page. - https://www.mkdocs.org/user-guide/plugins/#on_page_content + See: https://www.mkdocs.org/user-guide/plugins/#on_page_content - :param html: HTML rendered from Markdown source as string - :type html: str - :param page: mkdocs.nav.Page instance - :type page: Page - :param config: global configuration object - :type config: config_options.Config - :param files: global navigation object - :type files: [type] + Args: + html (str): HTML rendered from Markdown source as string + page (Page): `mkdocs.structure.pages.Page` instance + config (MkDocsConfig): global configuration object + files (Files): global files collection - :return: HTML rendered from Markdown source as string - :rtype: str + Returns: + Optional[str]: HTML rendered from Markdown source as string """ - # Skip if disabled if not self.config.enabled: return @@ -243,11 +249,11 @@ def on_page_content( # retrieve dates from git log page_dates = self.util.get_file_dates( in_page=page, - source_date_creation=self.src_date_created, - source_date_update=self.src_date_updated, - meta_datetime_format=self.meta_datetime_format, - meta_default_timezone=self.meta_default_timezone, - meta_default_time=self.meta_default_time, + source_date_creation=self.config.date_from_meta.as_creation, + source_date_update=self.config.date_from_meta.as_update, + meta_datetime_format=self.config.date_from_meta.datetime_format, + meta_default_timezone=self.config.date_from_meta.default_timezone, + meta_default_time=self.config.date_from_meta.default_time, ) # handle custom URL parameters diff --git a/tests/fixtures/mkdocs_complete.yml b/tests/fixtures/mkdocs_complete.yml index a2136ff..705fdcd 100644 --- a/tests/fixtures/mkdocs_complete.yml +++ b/tests/fixtures/mkdocs_complete.yml @@ -19,7 +19,6 @@ plugins: comments_path: "#__comments" date_from_meta: as_creation: "date" - as_update: false datetime_format: "%Y-%m-%d %H:%M" default_timezone: Europe/Paris default_time: "09:30" diff --git a/tests/fixtures/mkdocs_complete_no_git.yml b/tests/fixtures/mkdocs_complete_no_git.yml index 702ef47..ddda2b0 100644 --- a/tests/fixtures/mkdocs_complete_no_git.yml +++ b/tests/fixtures/mkdocs_complete_no_git.yml @@ -19,7 +19,6 @@ plugins: comments_path: "#__comments" date_from_meta: as_creation: "date" - as_update: false datetime_format: "%Y-%m-%d %H:%M" default_timezone: Europe/Paris default_time: "09:30" diff --git a/tests/fixtures/mkdocs_simple.yml b/tests/fixtures/mkdocs_simple.yml index 770cc75..9f2749c 100644 --- a/tests/fixtures/mkdocs_simple.yml +++ b/tests/fixtures/mkdocs_simple.yml @@ -17,7 +17,6 @@ plugins: - rss: date_from_meta: as_creation: "date" - as_update: false datetime_format: "%Y-%m-%d %H:%M" default_timezone: "Europe/Paris" default_time: "22:00" diff --git a/tests/test_config.py b/tests/test_config.py index 8908302..db5847a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -15,6 +15,7 @@ # Standard library import unittest +from datetime import datetime from pathlib import Path # 3rd party @@ -62,7 +63,13 @@ def test_plugin_config_defaults(self): "abstract_delimiter": "", "categories": None, "comments_path": None, - "date_from_meta": None, + "date_from_meta": { + "as_creation": "git", + "as_update": "git", + "datetime_format": "%Y-%m-%d %H:%M", + "default_time": datetime.min.strftime("%H:%M"), + "default_timezone": "UTC", + }, "enabled": True, "feed_ttl": 1440, "image": None, @@ -98,7 +105,13 @@ def test_plugin_config_image(self): "abstract_delimiter": "", "categories": None, "comments_path": None, - "date_from_meta": None, + "date_from_meta": { + "as_creation": "git", + "as_update": "git", + "datetime_format": "%Y-%m-%d %H:%M", + "default_time": datetime.min.strftime("%H:%M"), + "default_timezone": "UTC", + }, "enabled": True, "feed_ttl": 1440, "image": self.feed_image,