From bf65fddf0a7c79fef6ba34b1c95679aabd338877 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Sun, 9 Jun 2024 21:04:22 +0200 Subject: [PATCH 01/18] feature: allow multiple instances --- mkdocs_rss_plugin/plugin.py | 3 ++ tests/base.py | 25 ++++++++--- tests/dev/dev_load_config.py | 20 +++++++++ .../docs/blog/posts/sample_blog_post.md | 3 +- tests/fixtures/mkdocs_multiple_instances.yml | 16 +++++++ tests/test_build.py | 45 +++++++++++++++++++ 6 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 tests/dev/dev_load_config.py create mode 100644 tests/fixtures/mkdocs_multiple_instances.yml diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index 715ba62d..a0f43566 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -51,6 +51,9 @@ class GitRssPlugin(BasePlugin[RssPluginConfig]): """Main class for MkDocs plugin.""" + # allow to set the plugin multiple times in the same mkdocs config + supports_multiple_instances = True + def __init__(self): """Instanciation.""" # dates source diff --git a/tests/base.py b/tests/base.py index 1c133099..a5b51515 100644 --- a/tests/base.py +++ b/tests/base.py @@ -20,6 +20,9 @@ from mkdocs.config import load_config from mkdocs.config.base import Config +# package +from mkdocs_rss_plugin.plugin import GitRssPlugin + # ############################################################################# # ########## Classes ############### # ################################## @@ -45,19 +48,27 @@ def get_plugin_config_from_mkdocs( # instanciate plugin cfg_mkdocs = load_config(str(mkdocs_yml_filepath.resolve())) - plugins = cfg_mkdocs.get("plugins") - if "rss" not in plugins: + plugins = cfg_mkdocs.plugins + rss_plugin_instances = [ + plg for plg in plugins.items() if isinstance(plg[1], GitRssPlugin) + ] + if not len(rss_plugin_instances): logging.warning( f"Plugin {plugin_name} is not part of enabled plugin in the MkDocs " "configuration file: {mkdocs_yml_filepath}" ) - return {} - plugin_loaded = plugins.get("rss") + return cfg_mkdocs + + if len(rss_plugin_instances) == 1: + plugin = rss_plugin_instances[0][1] + self.assertIsInstance(plugin, GitRssPlugin) + elif len(rss_plugin_instances) >= 1: + plugin = rss_plugin_instances[1][1] + self.assertIsInstance(plugin, GitRssPlugin) - cfg = plugin_loaded.on_config(cfg_mkdocs) - logging.info("Fixture configuration loaded: " + str(cfg)) + logging.info(f"Fixture configuration loaded: {plugin.on_config(cfg_mkdocs)}") - return plugin_loaded.config + return plugin.config def build_docs_setup( self, diff --git a/tests/dev/dev_load_config.py b/tests/dev/dev_load_config.py new file mode 100644 index 00000000..3f132fc5 --- /dev/null +++ b/tests/dev/dev_load_config.py @@ -0,0 +1,20 @@ +from mkdocs.config import load_config + +from mkdocs_rss_plugin.plugin import GitRssPlugin + +mkdocs_cfg = load_config(config_file="tests/fixtures/mkdocs_multiple_instances.yml") + +print(mkdocs_cfg.plugins.keys()) +rss_instances = [ + plg for plg in mkdocs_cfg.plugins.items() if isinstance(plg[1], GitRssPlugin) +] +print(len(rss_instances)) + +for plg in mkdocs_cfg.plugins.items(): + print(plg) + print(isinstance(plg[1], GitRssPlugin)) + print(type(plg)) + +rss_instance_1 = plg[1] +print(dir(rss_instance_1)) +print(rss_instance_1.on_config(mkdocs_cfg)) diff --git a/tests/fixtures/docs/blog/posts/sample_blog_post.md b/tests/fixtures/docs/blog/posts/sample_blog_post.md index cf51740b..c8c42bce 100644 --- a/tests/fixtures/docs/blog/posts/sample_blog_post.md +++ b/tests/fixtures/docs/blog/posts/sample_blog_post.md @@ -1,6 +1,7 @@ --- date: 2023-02-12 -authors: [guts] +authors: + - guts categories: - Blog --- diff --git a/tests/fixtures/mkdocs_multiple_instances.yml b/tests/fixtures/mkdocs_multiple_instances.yml new file mode 100644 index 00000000..ad6e1fa0 --- /dev/null +++ b/tests/fixtures/mkdocs_multiple_instances.yml @@ -0,0 +1,16 @@ +site_name: Test Mkdocs with multiple RSS plugin instances +site_description: Multiple RSS plugin in a single mkdocs +site_url: https://guts.github.io/mkdocs-rss-plugin + +plugins: + - rss + - rss: + feeds_filenames: + json_created: blog.json + json_updated: blog-updated.json + rss_created: blog.xml + rss_updated: blog-updated.xml + match_path: "blog/.*" + +theme: + name: material diff --git a/tests/test_build.py b/tests/test_build.py index 75467e8d..a888d018 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -564,6 +564,51 @@ def test_simple_build_custom_output_basename(self): ) self.assertEqual(feed_parsed.bozo, 0) + def test_simple_build_multiple_instances(self): + config = self.get_plugin_config_from_mkdocs( + mkdocs_yml_filepath=Path("tests/fixtures/mkdocs_multiple_instances.yml"), + plugin_name="rss", + ) + + with tempfile.TemporaryDirectory() as tmpdirname: + cli_result = self.build_docs_setup( + testproject_path="docs", + mkdocs_yml_filepath=Path( + "tests/fixtures/mkdocs_multiple_instances.yml" + ), + output_path=tmpdirname, + strict=True, + ) + + if cli_result.exception is not None: + e = cli_result.exception + logger.debug(format_exception(type(e), e, e.__traceback__)) + + self.assertEqual(cli_result.exit_code, 0) + self.assertIsNone(cli_result.exception) + + # created items + feed_parsed = feedparser.parse( + Path(tmpdirname) / config.feeds_filenames.rss_created + ) + self.assertEqual(feed_parsed.bozo, 0) + + # updated items + feed_parsed = feedparser.parse( + Path(tmpdirname) / config.feeds_filenames.rss_updated + ) + self.assertEqual(feed_parsed.bozo, 0) + + # created items - blog + feed_parsed = feedparser.parse(Path(tmpdirname).joinpath("blog.xml")) + self.assertEqual(feed_parsed.bozo, 0) + + # updated items - blog + feed_parsed = feedparser.parse( + Path(tmpdirname).joinpath("blog-updated.xml") + ) + self.assertEqual(feed_parsed.bozo, 0) + def test_simple_build_pretty_print_enabled(self): with tempfile.TemporaryDirectory() as tmpdirname: cli_result = self.build_docs_setup( From 3e0d8d95c2d0fd12cdc0c2e475b968a8b9b2b5c8 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 13:05:30 +0200 Subject: [PATCH 02/18] improve(docs): order settings aZ and minor improvements on homepage --- docs/configuration.md | 184 ++++++++++++++++++++++-------------------- docs/index.md | 21 +++-- 2 files changed, 110 insertions(+), 95 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7de4e209..fb46adb8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -133,7 +133,7 @@ Output: For a sample see [homepage](./index.md#quickstart). -### `enabled`: enabling/disabling the plugin +### :material-toggle-switch: `enabled`: enabling/disabling the plugin { #enabled } You can use the `enabled` option to optionally disable this plugin. A possible use case is local development where you might want faster build times. It's recommended to use this option with an environment variable together with a default fallback (introduced in `mkdocs` v1.2.1, see [docs](https://www.mkdocs.org/user-guide/configuration/#environment-variables)). Example: @@ -152,7 +152,7 @@ mkdocs serve ---- -### `json_feed_enabled`: enabling/disabling export to JSON Feed { #json_feed_enabled } +### :simple-json: `json_feed_enabled`: enabling/disabling export to JSON Feed { #json_feed_enabled } Set it to `false` if you want to only export to RSS. @@ -160,7 +160,7 @@ Default: `true`. ---- -### `rss_feed_enabled`: enabling/disabling export to RSS { #rss_feed_enabled } +### :material-rss: `rss_feed_enabled`: enabling/disabling export to RSS { #rss_feed_enabled } Set it to `false` if you want to only export to JSON feed. @@ -168,77 +168,7 @@ Default: `true`. ---- -### `image`: set the channel image - -`image`: URL to image to use as feed illustration. - -Default: `None`. - -#### Example - -```yaml -plugins: - - rss: - image: https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Feed-icon.svg/128px-Feed-icon.svg.png -``` - -Output: - -```xml - - - https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Feed-icon.svg/128px-Feed-icon.svg.png - - MkDocs RSS Plugin - https://guts.github.io/mkdocs-rss-plugin/ - -``` - ----- - -### `comments_path`: item comments path - -`comments_path`: path to add to each item URL pointing. - -Default: `None`. - -For example, if you're using Material for Mkdocs with comment integration (Disqus or Isso), the comment block is under the div id `__comments`, so you can set: `comments_path: "#__comments"` and the output will be: - -```xml - - This page title is a perfect clickbait! - https://website.com/articles/best_article/ - https://website.com/articles/best_article/#__comments - [...] - - -``` - ----- - -### `length`: number of items to include in feed - -`length`: number of pages to include as feed items (entries). - -Default: `20` - ----- - -### `feed_ttl`: feed's cache time - -`feed_ttl`: number of minutes to be cached. Inserted as channel `ttl` element. See: [W3C RSS 2.0 documentation](https://www.w3schools.com/xml/rss_tag_ttl.asp). - -Default: `1440` (= 1 day) - -Output: - -```xml -1440 -``` - ----- - -### `abstract_chars_count`: item description length +### :material-more: `abstract_chars_count`: item description length { #abstract_chars_count } Used, in combination with `abstract_delimiter`, to determine each [item description element](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp): @@ -255,7 +185,7 @@ Default: `150` ---- -### `abstract_delimiter`: abstract delimiter +### :material-pen-minus: `abstract_delimiter`: abstract delimiter { #abstract_delimiter } Please see `abstract_chars_count` for how this setting is used. A value of `""` (the empty string) disables this step. @@ -265,13 +195,13 @@ Default: `` ---- -### `categories`: item categories +### :material-tag-multiple: `categories`: item categories { #categories } `categories`: list of page metadata values to use as [RSS item categories](https://www.w3schools.com/xml/rss_tag_category_item.asp). Default: `None`. -#### Example +#### Example { #categories_example } In configuration: @@ -327,7 +257,27 @@ Output: ---- -### `date_from_meta`: override dates from git log with page.meta +### :material-comment-bookmark-outline: `comments_path`: item comments path { #comments_path } + +`comments_path`: path to add to each item URL pointing. + +Default: `None`. + +For example, if you're using Material for Mkdocs with comment integration (Disqus or Isso), the comment block is under the div id `__comments`, so you can set: `comments_path: "#__comments"` and the output will be: + +```xml + + This page title is a perfect clickbait! + https://website.com/articles/best_article/ + https://website.com/articles/best_article/#__comments + [...] + + +``` + +---- + +### :material-calendar-start: `date_from_meta`: override dates from git log with page.meta { #date_from_meta } Basically, the plugin aims to retrieve creation and update dates from git log. But sometimes, it does not match the content workflow: markdown generated from sources, . @@ -339,7 +289,7 @@ So, it's possible to use the dates manually specified into the [page metadata] t - `default_timezone`: timezone to use by default to make aware datetimes. Default to `UTC`. Introduced in version 1.3.0 with [PR 142](https://github.com/Guts/mkdocs-rss-plugin/pull/142). - `default_time`: time to use if page contains only a date. Useful to avoid the 'midnight syndrome' or have to specify hour in every single page. Default to `None`. 24h-clock format is expected: `%H:%M`. Example: `"14:20"`. Introduced in version 1.4.0 with [PR 145](https://github.com/Guts/mkdocs-rss-plugin/pull/145). -#### Example +#### Example { #date_from_meta_example } For example, in your `best_article.md` created in 2019, you can write the front-matter like this: @@ -417,7 +367,7 @@ At the end, into the RSS you will get: ---- -### `feeds_filenames`: customize the output feed URL { #feeds_filenames } +### :material-alphabet-latin: `feeds_filenames`: customize the output feed URL { #feeds_filenames } > Since version 1.13.0. @@ -442,19 +392,57 @@ Default: ---- -### `pretty_print`: prettified XML +### :material-clock-end: `feed_ttl`: feed's cache time { #feed_ttl } -By default, the output file is minified, using Jinja2 strip options and manual work. It's possible to disable it and prettify the output using `pretty_print: true`. +`feed_ttl`: number of minutes to be cached. Inserted as channel `ttl` element. See: [W3C RSS 2.0 documentation](https://www.w3schools.com/xml/rss_tag_ttl.asp). + +Default: `1440` (= 1 day) + +Output: + +```xml +1440 +``` + +---- + +### :material-image-outline: `image`: set the channel image { #image } + +`image`: URL to image to use as feed illustration. + +Default: `None`. + +#### Example ```yaml plugins: - rss: - pretty_print: true + image: https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Feed-icon.svg/128px-Feed-icon.svg.png ``` -Default: `False`. +Output: + +```xml + + + https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Feed-icon.svg/128px-Feed-icon.svg.png + + MkDocs RSS Plugin + https://guts.github.io/mkdocs-rss-plugin/ + +``` -### `match_path`: filter pages to include in feed +---- + +### :material-counter: `length`: number of items to include in feed { #length } + +`length`: number of pages to include as feed items (entries). + +Default: `20` + +---- + +### :material-regex: `match_path`: filter pages to include in feed { #match_path } This adds a `match_path` option which should be a regex pattern matching the path to your files within the `docs_dir`. For example if you had a blog under `docs/blog` where `docs_dir` is `docs` you might use: @@ -474,7 +462,23 @@ plugins: Default: `.*`. -### `url_parameters`: additional URL parameters +---- + +### :material-format-indent-increase: `pretty_print`: prettified XML { #pretty_print } + +By default, the output file is minified, using Jinja2 strip options and manual work. It's possible to disable it and prettify the output using `pretty_print: true`. + +```yaml +plugins: + - rss: + pretty_print: true +``` + +Default: `False`. + +---- + +### :material-track-light: `url_parameters`: additional URL parameters { #url_parameters } This option allows you to add parameters to the URLs of the RSS feed items. It works as a dictionary of keys/values that is passed to [Python *urllib.parse.urlencode*](https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode). One possible use case is the addition of [Urchin Tracking Module (UTM) parameters](https://en.wikipedia.org/wiki/UTM_parameters): @@ -506,7 +510,9 @@ Will result in: Default: `None`. -### `use_git`: enable/disable git log +---- + +### :material-git: `use_git`: enable/disable git log { #use_git } If `false`, the plugin does not try use the git log nor does not check if this is a valid git repository and use informations exclusively from `page.meta` (YAML frontmatter). @@ -514,7 +520,9 @@ Useful if you build your documentation in an environment where you can't easily Default: `true`. -### `use_material_social_cards`: enable/disable integration with Material Social Cards plugin +---- + +### :material-cards: `use_material_social_cards`: enable/disable integration with Material Social Cards plugin { #use_material_social_cards } If `false`, the integration with Social Cards is disabled. diff --git a/docs/index.md b/docs/index.md index 2d253819..aa217e0c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -56,17 +56,24 @@ plugins: As examples, here come the feeds generated for this documentation: -- [feed_rss_created.xml](feed_rss_created.xml) for latest **created** pages: [W3C validator](https://validator.w3.org/feed/check.cgi?url=https%3A//guts.github.io/mkdocs-rss-plugin/feed_rss_created.xml) -- [feed_rss_updated.xml](feed_rss_updated.xml) for latest **updated** pages: [W3C validator](https://validator.w3.org/feed/check.cgi?url=https%3A//guts.github.io/mkdocs-rss-plugin/feed_rss_updated.xml) +- [feed_rss_created.xml](feed_rss_created.xml) and [feed_json_created.json](feed_json_created.json) for latest **created** pages: [W3C validator](https://validator.w3.org/feed/check.cgi?url=https%3A//guts.github.io/mkdocs-rss-plugin/feed_rss_created.xml) +- [feed_rss_updated.xml](feed_rss_updated.xml) and [feed_json_updated.json](feed_json_updated.json) for latest **updated** pages: [W3C validator](https://validator.w3.org/feed/check.cgi?url=https%3A//guts.github.io/mkdocs-rss-plugin/feed_rss_updated.xml) -Or it could be displayed as a Feedly follow button: +Or it could be displayed as a RSS or Feedly follow button: -[![Feedly button](https://s3.feedly.com/img/follows/feedly-follow-rectangle-flat-big_2x.png "Follow us on Feedly"){: width=130 height= 50 loading=lazy }](https://feedly.com/i/subscription/feed%2Fhttps%3A%2F%2Fguts.github.io%2Fmkdocs-rss-plugin%2Ffeed_rss_created.xml) +[![RSS logo](assets/rss_icon.svg "Subscribe to our RSS"){: width=130 loading=lazy }](https://guts.github.io/mkdocs-rss-plugin/feed_rss_created.xml) +[![Feedly button](https://s3.feedly.com/img/follows/feedly-follow-rectangle-flat-big_2x.png "Follow us on Feedly"){: width=130 loading=lazy }](https://feedly.com/i/subscription/feed%2Fhttps%3A%2F%2Fguts.github.io%2Fmkdocs-rss-plugin%2Ffeed_rss_created.xml) {: align=middle } For JSON Feed, you can use the icon: -[JSON Feed icon](https://jsonfeed.org/graphics/icon.png) +[![JSON Feed icon](https://jsonfeed.org/graphics/icon.png){: width=130 loading=lazy }](https://guts.github.io/mkdocs-rss-plugin/feed_json_created.json) +{: align=middle } + +!!! tip + See how to make your [RSS](integrations.md#reference-rss-feeds-in-html-meta-tags) and [JSON](integrations.md#reference-json-feeds-in-html-meta-tags) discoverable. + +---- ## Credits @@ -74,7 +81,7 @@ For JSON Feed, you can use the icon: - Plugin logic is inspired from [Tim Vink git-based plugins](https://github.com/timvink?tab=repositories&q=mkdocs-git&type=&language=) and main parts of Git stuff are nearly copied/pasted. - Using magic mainly from: - - [GitPython](https://gitpython.readthedocs.io/) - - [Jinja2](https://jinja.palletsprojects.com/) + - [GitPython](https://gitpython.readthedocs.io/) + - [Jinja2](https://jinja.palletsprojects.com/) - Documentation colors are a tribute to the classic RSS color scheme: orange and white. - Logo generated with DALLΒ·E. From 4a3a1dca0411f2ce99b02a410c60b395d32be76b Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 13:05:37 +0200 Subject: [PATCH 03/18] typo: indent --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 46caa300..5a1796aa 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,8 +56,8 @@ markdown_extensions: nav: - Home: index.md - Settings: - - configuration.md - - integrations.md + - configuration.md + - integrations.md - Contributing: contributing.md - API: api.md - Changelog: changelog.md From ce525e8f1fe9ed608e4d4acc1ba1354db11e5f31 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 13:16:58 +0200 Subject: [PATCH 04/18] docs: complete JSON schema --- docs/schema.json | 36 +++++++++++++++++++++++++++++++++--- mkdocs_rss_plugin/config.py | 2 +- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/schema.json b/docs/schema.json index 95be3e84..2896f313 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -1,6 +1,7 @@ { "$schema": "https://json-schema.org/draft-07/schema", - "title": "RSS feeds (i.e. a channel of items) using git log and page metadata.", + "title": "RSS and JSON feeds (i.e. a channel of items) using git log and page metadata.", + "description": "Support multiple instances in a single Mkdocs website.", "oneOf": [ { "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/", @@ -22,9 +23,15 @@ "default": 160, "minimum": -1 }, + "abstract_delimiter": { + "title": "String to mark where the description ends. Also called excerpt.", + "default": "", + "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#abstract_delimiter", + "type": "string" + }, "categories": { "title": "List of page metadata keys to use as item categories.", - "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#categories-item-categories", + "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#categories", "type": "array", "default": null, "items": { @@ -35,7 +42,7 @@ }, "comments_path": { "title": "Part of URL to the items' comment div.", - "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#comments_path-item-comments-path", + "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#comments_path", "type": "string", "default": null, "format": "uri-reference" @@ -98,6 +105,29 @@ "type": "integer", "default": 1440 }, + "feeds_filenames": { + "title": "Customize output RSS and JSON feeds names.", + "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#feeds_filenames", + "type": "object", + "properties": { + "json_created": { + "default": "feed_json_created.json", + "type": "string" + }, + "json_updated": { + "default": "feed_json_updated.json", + "type": "string" + }, + "rss_created": { + "default": "feed_rss_created.xml", + "type": "string" + }, + "rss_updated": { + "default": "feed_rss_updated.xml", + "type": "string" + } + } + }, "image": { "title": "Feed channel illustration", "markdownDescription": "https://guts.github.io/mkdocs-rss-plugin/configuration/#image-set-the-channel-image", diff --git a/mkdocs_rss_plugin/config.py b/mkdocs_rss_plugin/config.py index b2129bdb..e335d587 100644 --- a/mkdocs_rss_plugin/config.py +++ b/mkdocs_rss_plugin/config.py @@ -33,11 +33,11 @@ class RssPluginConfig(Config): date_from_meta = config_options.Optional(config_options.Type(dict)) enabled = config_options.Type(bool, default=True) feed_ttl = config_options.Type(int, default=1440) + feeds_filenames = config_options.SubConfig(_FeedsFilenamesConfig) image = config_options.Optional(config_options.Type(str)) json_feed_enabled = config_options.Type(bool, default=True) length = config_options.Type(int, default=20) match_path = config_options.Type(str, default=".*") - feeds_filenames = config_options.SubConfig(_FeedsFilenamesConfig) pretty_print = config_options.Type(bool, default=False) rss_feed_enabled = config_options.Type(bool, default=True) url_parameters = config_options.Optional(config_options.Type(dict)) From ca7f73eda5c31e96988eb8e8db0e32fe51853fdc Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 13:34:56 +0200 Subject: [PATCH 05/18] chore(deps): set minimum Mkdocs version to 1.4 Due to configuration objects improvements. --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 1600f68c..1fa26c66 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -3,6 +3,6 @@ GitPython>=3.1,<3.2 -mkdocs>=1.4,<2 +mkdocs>=1.5,<2 pytz==2022.* ; python_version < "3.9" tzdata==2024.* ; python_version >= "3.9" and sys_platform == "win32" From 3bcaca393c3292d61886eceebfa8020da4f872a8 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 13:44:49 +0200 Subject: [PATCH 06/18] docs: fix internal links --- docs/configuration.md | 2 +- docs/integrations.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index fb46adb8..a8694044 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -49,7 +49,7 @@ Basically, each page is an item element in the RSS feed. | :------ | :------------: | :------------------------ | | [`page.canonical_url`](https://github.com/mkdocs/mkdocs/blob/master/mkdocs/structure/pages.py#L97-L105) | **required** and *optional* | mandatory item element [`link`](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp) and also used as [`guid`](https://www.w3schools.com/xml/rss_tag_guid.asp) | | [`page.meta.title`](https://www.mkdocs.org/user-guide/writing-your-docs/#yaml-style-meta-data) | **required** | [`title`](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp) | -| [`page.meta.description`](https://www.mkdocs.org/user-guide/writing-your-docs/#yaml-style-meta-data) if present, else extract headlines from the content. See below the [item_description_length option](#item-description-length). | **required** | [`description`](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp) | +| [`page.meta.description`](https://www.mkdocs.org/user-guide/writing-your-docs/#yaml-style-meta-data) if present, else extract headlines from the content. See below the [item_description_length option](#abstract_chars_count). | **required** | [`description`](https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp) | | creation or last update datetime according git log. Can be overridden by dates in page.meta. If not, then it uses [MkDocs build date](https://github.com/mkdocs/mkdocs/blob/master/mkdocs/utils/__init__.py#L111-L118) | **recommended** | [`pubDate`](https://www.w3schools.com/xml/rss_tag_pubdate_item.asp) | | [`page.meta.image`](https://www.mkdocs.org/user-guide/writing-your-docs/#yaml-style-meta-data) | *optional* | item element [`enclosure`](https://www.w3schools.com/xml/rss_tag_enclosure.asp). Some HTTP requests can be performed to retrieve remote images length. | | [`page.meta.authors`](https://www.mkdocs.org/user-guide/writing-your-docs/#yaml-style-meta-data) or `page.meta.author`. Accepted value types: `str` `list`, `tuple`.
To comply with the standard, the page writer is responsible to fill this field following this syntax: `john@doe.com (John Doe)` ([read this SO](https://stackoverflow.com/a/6079731/2556577)). | *optional* | [`author`](https://www.w3schools.com/XML/rss_tag_author.asp) | diff --git a/docs/integrations.md b/docs/integrations.md index 2b6e3dc4..5d4997ff 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -15,7 +15,7 @@ Here's how the RSS plugin prioritizes the image to be used in the feed: If you don't want this integration, you can disable it with the option: `use_material_social_cards=false`. -> See [related section in settings](./configuration.md#use_material_social_cards-enabledisable-integration-with-material-social-cards-plugin). +> See [related section in settings](./configuration.md#use_material_social_cards). ---- From d6eae35b59ec248968ec7a33f0519b3e7074ce3d Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 17:14:44 +0200 Subject: [PATCH 07/18] tooling: bump minimal Python version to 3.10 --- .pre-commit-config.yaml | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0de88fc6..2bd5bd6b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,27 +1,29 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks -exclude: "node_modules|migrations|.venv|.direnv|tests/dev/|tests/fixtures/" +exclude: ".venv|.direnv|tests/dev/|tests/fixtures/" fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: - id: check-added-large-files - args: ["--maxkb=500"] + args: + - --maxkb=500 - id: check-ast - id: check-builtin-literals - id: check-case-conflict - id: check-json - id: check-toml - id: check-yaml - args: [--unsafe] + args: + - --unsafe - id: detect-private-key - id: end-of-file-fixer - id: fix-byte-order-marker - id: fix-encoding-pragma - args: [--remove] + args: + - --remove - id: trailing-whitespace - args: [--markdown-linebreak-ext=md] + args: + - --markdown-linebreak-ext=md - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 @@ -33,32 +35,40 @@ repos: hooks: - id: pyupgrade args: - - "--py38-plus" + - "--py310-plus" - repo: https://github.com/astral-sh/ruff-pre-commit rev: "v0.4.7" hooks: - id: ruff - args: ["--fix-only", "--target-version=py38"] + args: + - --fix-only + - --target-version=py310 - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: - id: isort - args: ["--profile", "black", "--filter-files"] + args: + - --profile + - black + - --filter-files - repo: https://github.com/psf/black rev: 24.4.2 hooks: - id: black - args: ["--target-version=py38"] + args: + - --target-version=py310 - repo: https://github.com/pycqa/flake8 rev: 7.0.0 hooks: - id: flake8 language: python - args: ["--config=setup.cfg", "--select=E9,F63,F7,F82"] + args: + - --config=setup.cfg + - --select=E9,F63,F7,F82 ci: autofix_prs: true From a8138f9fd7d353f92b8d7cc30c89f4c17d9e9078 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 17:15:18 +0200 Subject: [PATCH 08/18] chore(packaging): remove support of Python 3.8 and 3.9 --- .github/workflows/lint-and-tests.yml | 2 -- setup.py | 7 ++----- sonar-project.properties | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/lint-and-tests.yml b/.github/workflows/lint-and-tests.yml index 44f22db1..6a2820db 100644 --- a/.github/workflows/lint-and-tests.yml +++ b/.github/workflows/lint-and-tests.yml @@ -22,8 +22,6 @@ jobs: fail-fast: false matrix: python-version: - - "3.8" - - "3.9" - "3.10" - "3.11" - "3.12" diff --git a/setup.py b/setup.py index 76b4b2a4..a245d3b1 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,6 @@ # standard library from pathlib import Path -from typing import List, Union # 3rd party from setuptools import find_packages, setup @@ -26,7 +25,7 @@ # ################################## -def load_requirements(requirements_files: Union[Path, List[Path]]) -> list: +def load_requirements(requirements_files: Path | list[Path]) -> list: """Helper to load requirements list from a path or a list of paths. Args: @@ -79,7 +78,7 @@ def load_requirements(requirements_files: Union[Path, List[Path]]) -> list: # run entry_points={"mkdocs.plugins": ["rss = mkdocs_rss_plugin.plugin:GitRssPlugin"]}, # dependencies - python_requires=">=3.8, <4", + python_requires=">=3.10, <4", extras_require={ # tooling "dev": load_requirements(HERE / "requirements/development.txt"), @@ -93,8 +92,6 @@ def load_requirements(requirements_files: Union[Path, List[Path]]) -> list: "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/sonar-project.properties b/sonar-project.properties index dd181754..d33fa5c0 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,7 +6,7 @@ sonar.projectKey=Guts_mkdocs-rss-plugin # only=main # Python versions -sonar.python.version=3.8, 3.9, 3.10, 3.11, 3.12 +sonar.python.version=3.10, 3.11, 3.12 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=mkdocs_rss_plugin From 8ec616461c91038cdf173f9aaa45cd78fe303ded Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 17:16:22 +0200 Subject: [PATCH 09/18] refacto(python): apply git hooks with Python >= 3.10 --- .../theme_material_social_plugin.py | 5 ++-- mkdocs_rss_plugin/models.py | 24 +++++++-------- mkdocs_rss_plugin/plugin.py | 9 +++--- mkdocs_rss_plugin/util.py | 29 +++++++++---------- tests/test_build.py | 16 ++++++---- 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/mkdocs_rss_plugin/integrations/theme_material_social_plugin.py b/mkdocs_rss_plugin/integrations/theme_material_social_plugin.py index a8f6e42e..f9321123 100644 --- a/mkdocs_rss_plugin/integrations/theme_material_social_plugin.py +++ b/mkdocs_rss_plugin/integrations/theme_material_social_plugin.py @@ -6,7 +6,6 @@ # standard library from pathlib import Path -from typing import Optional # 3rd party from mkdocs.config.config_options import Config @@ -174,7 +173,7 @@ def get_social_cards_dir(self, mkdocs_config: Config) -> str: return social_plugin_cfg.config.cards_dir def get_social_card_build_path_for_page( - self, mkdocs_page: Page, mkdocs_site_dir: Optional[str] = None + self, mkdocs_page: Page, mkdocs_site_dir: str | None = None ) -> Path: """Get social card URL for a specific page in documentation. @@ -197,7 +196,7 @@ def get_social_card_build_path_for_page( def get_social_card_url_for_page( self, mkdocs_page: Page, - mkdocs_site_url: Optional[str] = None, + mkdocs_site_url: str | None = None, ) -> str: """Get social card URL for a specific page in documentation. diff --git a/mkdocs_rss_plugin/models.py b/mkdocs_rss_plugin/models.py index 7e863bd8..bdc68664 100644 --- a/mkdocs_rss_plugin/models.py +++ b/mkdocs_rss_plugin/models.py @@ -7,7 +7,7 @@ # standard from datetime import datetime from pathlib import Path -from typing import NamedTuple, Optional +from typing import NamedTuple # ############################################################################ @@ -16,14 +16,14 @@ class PageInformation(NamedTuple): """Data type to set and get page information in order to produce the RSS feed.""" - abs_path: Optional[Path] = None - categories: Optional[list] = None - authors: Optional[tuple] = None - created: Optional[datetime] = None - description: Optional[str] = None - guid: Optional[str] = None - image: Optional[str] = None - title: Optional[str] = None - updated: Optional[datetime] = None - url_comments: Optional[str] = None - url_full: Optional[str] = None + abs_path: Path | None = None + categories: list | None = None + authors: tuple | None = None + created: datetime | None = None + description: str | None = None + guid: str | None = None + image: str | None = None + title: str | None = None + updated: datetime | None = None + url_comments: str | None = None + url_full: str | None = None diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index a0f43566..4e8f18be 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -11,7 +11,6 @@ from email.utils import formatdate from pathlib import Path from re import compile -from typing import Optional # 3rd party from jinja2 import Environment, FileSystemLoader, select_autoescape @@ -58,9 +57,9 @@ def __init__(self): """Instanciation.""" # dates source self.src_date_created = self.src_date_updated = "git" - self.meta_datetime_format: Optional[str] = None + self.meta_datetime_format: str | None = None self.meta_default_timezone: str = "UTC" - self.meta_default_time: Optional[datetime.time] = None + self.meta_default_time: datetime.time | None = None # pages storage self.pages_to_filter: list = [] # prepare output feeds @@ -208,7 +207,7 @@ def on_config(self, config: config_options.Config) -> dict: @event_priority(priority=-75) def on_page_content( self, html: str, page: Page, config: config_options.Config, files - ) -> Optional[str]: + ) -> 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. @@ -297,7 +296,7 @@ def on_page_content( ) ) - def on_post_build(self, config: config_options.Config) -> Optional[dict]: + def on_post_build(self, config: config_options.Config) -> dict | None: """The post_build event does not alter any variables. \ Use this event to call post-build scripts. \ See: diff --git a/mkdocs_rss_plugin/util.py b/mkdocs_rss_plugin/util.py index 426c7cee..07d45866 100644 --- a/mkdocs_rss_plugin/util.py +++ b/mkdocs_rss_plugin/util.py @@ -7,12 +7,12 @@ # standard library import logging import ssl -import sys +from collections.abc import Iterable from datetime import date, datetime from email.utils import format_datetime from mimetypes import guess_type from pathlib import Path -from typing import Any, Iterable, Optional, Tuple, Union +from typing import Any from urllib import request from urllib.error import HTTPError, URLError from urllib.parse import urlencode, urlparse, urlunparse @@ -33,10 +33,7 @@ ) # conditional imports -if sys.version_info < (3, 9): - from mkdocs_rss_plugin.timezoner_pre39 import set_datetime_zoneinfo -else: - from mkdocs_rss_plugin.timezoner_py39 import set_datetime_zoneinfo +from mkdocs_rss_plugin.timezoner_py39 import set_datetime_zoneinfo # ############################################################################ # ########## Globals ############# @@ -58,9 +55,9 @@ def __init__( self, path: str = ".", use_git: bool = True, - integration_material_social_cards: Optional[ + integration_material_social_cards: None | ( IntegrationMaterialSocialCards - ] = None, + ) = None, ): """Class hosting the plugin logic. @@ -139,7 +136,7 @@ def build_url(self, base_url: str, path: str, args_dict: dict = None) -> str: url_parts[4] = urlencode(args_dict) return urlunparse(url_parts) - def get_value_from_dot_key(self, data: dict, dot_key: Union[str, bool]) -> Any: + def get_value_from_dot_key(self, data: dict, dot_key: str | bool) -> Any: """ Retrieves a value from a dictionary using a dot notation key. @@ -168,8 +165,8 @@ def get_file_dates( source_date_update: str = "git", meta_datetime_format: str = "%Y-%m-%d %H:%M", meta_default_timezone: str = "UTC", - meta_default_time: Optional[datetime] = None, - ) -> Tuple[datetime, datetime]: + meta_default_time: datetime | None = None, + ) -> tuple[datetime, datetime]: """Extract creation and update dates from page metadata (yaml frontmatter) or git log for given file. @@ -325,7 +322,7 @@ def get_file_dates( get_build_datetime(), ) - def get_authors_from_meta(self, in_page: Page) -> Optional[Tuple[str]]: + def get_authors_from_meta(self, in_page: Page) -> tuple[str] | None: """Returns authors from page meta. It handles 'author' and 'authors' for keys, \ str and iterable as values types. @@ -508,7 +505,7 @@ def get_description_or_abstract( ) return "" - def get_image(self, in_page: Page, base_url: str) -> Optional[Tuple[str, str, int]]: + def get_image(self, in_page: Page, base_url: str) -> tuple[str, str, int] | None: """Get page's image from page meta or social cards and returns properties. Args: @@ -610,7 +607,7 @@ def get_remote_image_length( http_method: str = "HEAD", attempt: int = 0, ssl_context: ssl.SSLContext = None, - ) -> Optional[int]: + ) -> int | None: """Retrieve length for remote images (starting with 'http' \ in meta.image or meta.illustration). \ It tries to perform a HEAD request and get the length from the headers. \ @@ -662,7 +659,7 @@ def get_remote_image_length( return int(img_length) @staticmethod - def get_site_url(mkdocs_config: Config) -> Optional[str]: + def get_site_url(mkdocs_config: Config) -> str | None: """Extract site URL from MkDocs configuration and enforce the behavior to ensure \ returning a str with length > 0 or None. If exists, it adds an ending slash. @@ -688,7 +685,7 @@ def get_site_url(mkdocs_config: Config) -> Optional[str]: return site_url - def guess_locale(self, mkdocs_config: Config) -> Optional[str]: + def guess_locale(self, mkdocs_config: Config) -> str | None: """Extract language code from MkDocs or Theme configuration. :param mkdocs_config: configuration object diff --git a/tests/test_build.py b/tests/test_build.py index a888d018..3dcef847 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -699,16 +699,20 @@ def test_json_feed_validation(self): self.assertIsNone(cli_result.exception) # created items - with Path(tmpdirname).joinpath(OUTPUT_JSON_FEED_CREATED).open( - "r", encoding="UTF-8" - ) as in_json: + with ( + Path(tmpdirname) + .joinpath(OUTPUT_JSON_FEED_CREATED) + .open("r", encoding="UTF-8") as in_json + ): json_feed_created_data = json.load(in_json) jsonfeed.Feed.parse(json_feed_created_data) # updated items - with Path(tmpdirname).joinpath(OUTPUT_JSON_FEED_UPDATED).open( - "r", encoding="UTF-8" - ) as in_json: + with ( + Path(tmpdirname) + .joinpath(OUTPUT_JSON_FEED_UPDATED) + .open("r", encoding="UTF-8") as in_json + ): json_feed_updated_data = json.load(in_json) jsonfeed.Feed.parse(json_feed_updated_data) From 2894561f013e973c9872951914408d6b28062c77 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 16:41:42 +0200 Subject: [PATCH 10/18] quality: rename compile into re_compile to avoid shadowing Python built-in --- mkdocs_rss_plugin/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index 4e8f18be..46a1208d 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -10,7 +10,8 @@ from datetime import datetime from email.utils import formatdate from pathlib import Path -from re import compile +from re import compile as re_compile +from typing import Optional # 3rd party from jinja2 import Environment, FileSystemLoader, select_autoescape @@ -131,7 +132,7 @@ 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: From cf394ceb113e16cd96b5bb0c7cde5b4fb53eea4f Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 16:42:12 +0200 Subject: [PATCH 11/18] fix: json_url was not set when site_url is missing --- mkdocs_rss_plugin/plugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index 46a1208d..80671ac6 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -200,7 +200,9 @@ 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 From 05c4b4888307fb486a33ae8d0bea1eaddabc0fde Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 16:58:01 +0200 Subject: [PATCH 12/18] imrpove: base_path expect an array --- mkdocs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 5a1796aa..8641abc5 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: From 473557e89792dd8affd6ec269d734ad3b45fa45d Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 16:59:16 +0200 Subject: [PATCH 13/18] refacto(config): modernize date_from_meta option switching to an object-based SubConfig --- mkdocs_rss_plugin/config.py | 15 +++- mkdocs_rss_plugin/plugin.py | 146 ++++++++++++++++++------------------ tests/test_config.py | 17 ++++- 3 files changed, 104 insertions(+), 74 deletions(-) diff --git a/mkdocs_rss_plugin/config.py b/mkdocs_rss_plugin/config.py index e335d587..49d7482a 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 80671ac6..6f53e13e 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -16,8 +16,10 @@ # 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 @@ -56,30 +58,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: @@ -135,45 +135,53 @@ def on_config(self, config: config_options.Config) -> dict: 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) @@ -209,27 +217,23 @@ def on_config(self, config: config_options.Config) -> dict: @event_priority(priority=-75) def on_page_content( - self, html: str, page: Page, config: config_options.Config, 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. - - 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] - - :return: HTML rendered from Markdown source as string - :rtype: str + self, html: str, page: Page, config: MkDocsConfig, files: Files + ) -> Optional[str]: + """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. + + See: https://www.mkdocs.org/user-guide/plugins/#on_page_content + + 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 + + Returns: + Optional[str]: HTML rendered from Markdown source as string """ - # Skip if disabled if not self.config.enabled: return @@ -246,11 +250,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/test_config.py b/tests/test_config.py index 89083028..db5847a7 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, From 6d0ab3ee91400b0c94ca24894005c1790cadd565 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 17:06:57 +0200 Subject: [PATCH 14/18] refacto: remove deprecated date_from_meta.as_* bool --- mkdocs.yml | 1 - tests/fixtures/mkdocs_complete.yml | 1 - tests/fixtures/mkdocs_complete_no_git.yml | 1 - tests/fixtures/mkdocs_simple.yml | 1 - 4 files changed, 4 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 8641abc5..6a516d7b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -106,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/tests/fixtures/mkdocs_complete.yml b/tests/fixtures/mkdocs_complete.yml index a2136ffc..705fdcd3 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 702ef476..ddda2b06 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 770cc750..9f2749cb 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" From a5db57fc9582c670410c97296cba53d2ace9e540 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 15:26:05 +0000 Subject: [PATCH 15/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mkdocs_rss_plugin/plugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mkdocs_rss_plugin/plugin.py b/mkdocs_rss_plugin/plugin.py index 6f53e13e..0d5f8737 100644 --- a/mkdocs_rss_plugin/plugin.py +++ b/mkdocs_rss_plugin/plugin.py @@ -11,7 +11,6 @@ from email.utils import formatdate from pathlib import Path from re import compile as re_compile -from typing import Optional # 3rd party from jinja2 import Environment, FileSystemLoader, select_autoescape @@ -218,7 +217,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig: @event_priority(priority=-75) def on_page_content( self, html: str, page: Page, config: MkDocsConfig, files: Files - ) -> Optional[str]: + ) -> 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. From dad7b254bf6a80c7ed80a3ea93a147ab0479f436 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 21:22:28 +0200 Subject: [PATCH 16/18] release: bump version to 1.13.0 --- .mailmap | 8 ++++++++ CHANGELOG.md | 26 ++++++++++++++++++++++++++ mkdocs_rss_plugin/__about__.py | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..7f09a1de --- /dev/null +++ b/.mailmap @@ -0,0 +1,8 @@ +Dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +Dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> <27856297+dependabot-preview[bot]@users.noreply.github.com> + +Julien M. +Julien M. + +Y.D.X. <73375426+YDX-2147483647@users.noreply.github.com> +Y.D.X. <73375426+YDX-2147483647@users.noreply.github.com> <73375426+YDX-2147483647@users.noreply.github.com> diff --git a/CHANGELOG.md b/CHANGELOG.md index 181e52cc..242668d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --> +## 1.13.0 - 2024-06-10 + +### Bugs fixes πŸ› + +### Features and enhancements πŸŽ‰ + +* feature: allow customize output filenames by @Guts in +* feature: allow multiple instances by @Guts in +* chore: set minimal python version to 3.10 by @Guts in +* chore(deps): set minimum Mkdocs version to 1.4 by @Guts in +* Refacto: modernize config date from meta by @Guts in + +### Tooling πŸ”§ + +* ci: use trusted publisher and remove token by @Guts in +* tooling: add SonarCloud config by @Guts in + +### Documentation πŸ“– + +* Documentation: improve guide how to make JSON feed discoverable by @Guts in +* Docs: order settings aZ and minor improvements by @Guts in +* docs: complete JSON schema with latest features by @Guts in +* docs: fix internal links by @Guts in + +---- + ## 1.12.2 - 2024-04-30 ### Bugs fixes πŸ› diff --git a/mkdocs_rss_plugin/__about__.py b/mkdocs_rss_plugin/__about__.py index 748039ba..abd7812b 100644 --- a/mkdocs_rss_plugin/__about__.py +++ b/mkdocs_rss_plugin/__about__.py @@ -40,7 +40,7 @@ __title_clean__ = "".join(e for e in __title__ if e.isalnum()) __uri__ = "https://github.com/Guts/mkdocs-rss-plugin/" -__version__ = "1.12.2" +__version__ = "1.13.0" __version_info__ = tuple( [ int(num) if num.isdigit() else num From fa678873041900dc84ee082e7e8e498eddfbe7ae Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 10 Jun 2024 22:52:43 +0200 Subject: [PATCH 17/18] fix: typo in logs --- mkdocs_rss_plugin/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs_rss_plugin/util.py b/mkdocs_rss_plugin/util.py index 07d45866..6e56ade6 100644 --- a/mkdocs_rss_plugin/util.py +++ b/mkdocs_rss_plugin/util.py @@ -554,9 +554,9 @@ def get_image(self, in_page: Page, base_url: str) -> tuple[str, str, int] | None else: logger.debug( f"Social card: {img_local_path} still not exists. Trying to " - f"retrieve length from remote image: {img_url}" + f"retrieve length from remote image: {img_url}. " "Note that would work only if the social card image has been " - "published before)." + "already published before the build." ) img_length = self.get_remote_image_length(image_url=img_url) From c86a1934f9c17dda47af7a3f3aee8f58e7564203 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Tue, 11 Jun 2024 10:24:37 +0200 Subject: [PATCH 18/18] refacto(cleanup): remove python 3.9 related code and deps (timezone) --- .../{timezoner_py39.py => timezoner.py} | 0 mkdocs_rss_plugin/timezoner_pre39.py | 56 ------------------- mkdocs_rss_plugin/util.py | 4 +- requirements/base.txt | 1 - 4 files changed, 1 insertion(+), 60 deletions(-) rename mkdocs_rss_plugin/{timezoner_py39.py => timezoner.py} (100%) delete mode 100644 mkdocs_rss_plugin/timezoner_pre39.py diff --git a/mkdocs_rss_plugin/timezoner_py39.py b/mkdocs_rss_plugin/timezoner.py similarity index 100% rename from mkdocs_rss_plugin/timezoner_py39.py rename to mkdocs_rss_plugin/timezoner.py diff --git a/mkdocs_rss_plugin/timezoner_pre39.py b/mkdocs_rss_plugin/timezoner_pre39.py deleted file mode 100644 index 73ae7e5b..00000000 --- a/mkdocs_rss_plugin/timezoner_pre39.py +++ /dev/null @@ -1,56 +0,0 @@ -#! python3 # noqa: E265 - - -""" - Manage timezones for pages date(time)s using pytz module. - Meant to be dropped when Python 3.8 reaches EOL. -""" - -# ############################################################################ -# ########## Libraries ############# -# ################################## - -# standard library -from datetime import datetime - -# 3rd party -import pytz -from mkdocs.plugins import get_plugin_logger - -# package -from mkdocs_rss_plugin.constants import MKDOCS_LOGGER_NAME - -# ############################################################################ -# ########## Globals ############# -# ################################ - - -logger = get_plugin_logger(MKDOCS_LOGGER_NAME) - - -# ############################################################################ -# ########## Functions ########### -# ################################ - - -def set_datetime_zoneinfo( - input_datetime: datetime, config_timezone: str = "UTC" -) -> datetime: - """Apply timezone to a naive datetime. - - :param input_datetime: offset-naive datetime - :type input_datetime: datetime - :param config_timezone: name of timezone as registered in IANA database, - defaults to "UTC". Example : Europe/Paris. - :type config_timezone: str, optional - - :return: offset-aware datetime - :rtype: datetime - """ - if input_datetime.tzinfo: - return input_datetime - elif not config_timezone: - return input_datetime.replace(tzinfo=pytz.utc) - else: - config_tz = pytz.timezone(config_timezone) - return config_tz.localize(input_datetime) diff --git a/mkdocs_rss_plugin/util.py b/mkdocs_rss_plugin/util.py index 6e56ade6..e966fe0e 100644 --- a/mkdocs_rss_plugin/util.py +++ b/mkdocs_rss_plugin/util.py @@ -31,9 +31,7 @@ from mkdocs_rss_plugin.integrations.theme_material_social_plugin import ( IntegrationMaterialSocialCards, ) - -# conditional imports -from mkdocs_rss_plugin.timezoner_py39 import set_datetime_zoneinfo +from mkdocs_rss_plugin.timezoner import set_datetime_zoneinfo # ############################################################################ # ########## Globals ############# diff --git a/requirements/base.txt b/requirements/base.txt index 1fa26c66..fc74df58 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,5 +4,4 @@ GitPython>=3.1,<3.2 mkdocs>=1.5,<2 -pytz==2022.* ; python_version < "3.9" tzdata==2024.* ; python_version >= "3.9" and sys_platform == "win32"