render-engine 2025.11.1a1__tar.gz → 2026.1.1a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/workflows/devcontainer-ci.yml +2 -2
  2. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/workflows/labeler.yml +1 -1
  3. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/workflows/lint.yml +2 -2
  4. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/workflows/publish.yml +1 -1
  5. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/workflows/scorecard.yml +3 -3
  6. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/workflows/test.yml +3 -3
  7. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/PKG-INFO +1 -1
  8. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/collection.md +2 -1
  9. render_engine-2026.1.1a1/docs/docs/content_manager.md +67 -0
  10. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/mkdocs.yml +1 -0
  11. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/requirements.txt +35 -35
  12. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/__version__.py +3 -3
  13. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/_base_object.py +1 -0
  14. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/collection.py +40 -22
  15. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/page.py +5 -5
  16. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine.egg-info/PKG-INFO +1 -1
  17. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine.egg-info/SOURCES.txt +1 -0
  18. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.all-contributorsrc +0 -0
  19. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.devcontainer/Dockerfile +0 -0
  20. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.devcontainer/devcontainer.json +0 -0
  21. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.devcontainer/setup.sh +0 -0
  22. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/FUNDING.yml +0 -0
  23. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/ISSUE_TEMPLATE/config.yaml +0 -0
  24. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/ISSUE_TEMPLATE/form_issue_template.yml +0 -0
  25. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  26. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/dependabot-bot.yml +0 -0
  27. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/dependabot.yml +0 -0
  28. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/labeler.yml +0 -0
  29. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.github/release.yml +0 -0
  30. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.gitignore +0 -0
  31. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.markdownlint.json +0 -0
  32. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.pre-commit-config.yaml +0 -0
  33. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.readthedocs.yml +0 -0
  34. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/.vscode/tasks.json +0 -0
  35. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/CONTRIBUTING.md +0 -0
  36. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/README.md +0 -0
  37. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/SECURITY.md +0 -0
  38. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/.markdownlint.json +0 -0
  39. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/archive.md +0 -0
  40. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/assets/create environment vs code.gif +0 -0
  41. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/assets/create-app-help.png +0 -0
  42. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/assets/create-codespace.gif +0 -0
  43. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/assets/launching a dev container.gif +0 -0
  44. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/assets/render-engine-init-help.png +0 -0
  45. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/assets/render-engine-init.png +0 -0
  46. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/cli.md +0 -0
  47. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/contributing/CODE_OF_CONDUCT.md +0 -0
  48. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/contributing/CONTRIBUTING.md +0 -0
  49. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/contributing/environment_setup.md +0 -0
  50. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/custom_collections.md +0 -0
  51. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/feeds.md +0 -0
  52. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/getting-started/building-your-site.md +0 -0
  53. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/getting-started/creating-a-collection.md +0 -0
  54. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/getting-started/creating-a-page.md +0 -0
  55. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/getting-started/creating-your-app.md +0 -0
  56. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/getting-started/getting-started.md +0 -0
  57. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/getting-started/installation.md +0 -0
  58. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/getting-started/layout.md +0 -0
  59. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/index.md +0 -0
  60. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/page.md +0 -0
  61. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/parsers.md +0 -0
  62. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/plugins.md +0 -0
  63. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/site.md +0 -0
  64. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/site_map.md +0 -0
  65. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/templates.md +0 -0
  66. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/docs/theme_management.md +0 -0
  67. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/docs/requirements.txt +0 -0
  68. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/noxfile.py +0 -0
  69. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/pyproject.toml +0 -0
  70. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/setup.cfg +0 -0
  71. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/.gitignore +0 -0
  72. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/__init__.py +0 -0
  73. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/__main__.py +0 -0
  74. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/archive.py +0 -0
  75. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/blog.py +0 -0
  76. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/content_managers/__init__.py +0 -0
  77. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/content_managers/base_content_manager.py +0 -0
  78. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/content_managers/file_content_manager.py +0 -0
  79. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/engine.py +0 -0
  80. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/extras/__init__.py +0 -0
  81. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/feeds.py +0 -0
  82. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/hookspecs.py +0 -0
  83. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/links.py +0 -0
  84. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/parsers/markdown.py +0 -0
  85. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/plugins.py +0 -0
  86. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/py.typed +0 -0
  87. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/__init__.py +0 -0
  88. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/archive.html +0 -0
  89. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/base.html +0 -0
  90. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/base_collection_path.md +0 -0
  91. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/base_templates/_archive.html +0 -0
  92. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/base_templates/_base.html +0 -0
  93. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/base_templates/_page.html +0 -0
  94. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/components/footer.html +0 -0
  95. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/components/page_title.html +0 -0
  96. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/page.html +0 -0
  97. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/rss2.0.xml +0 -0
  98. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/rss2.0_items.xml +0 -0
  99. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/sitemap.xml +0 -0
  100. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/render_engine_templates/sitemap_item.xml +0 -0
  101. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/site.py +0 -0
  102. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/site_map.py +0 -0
  103. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/themes.py +0 -0
  104. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine/utils/__init__.py +0 -0
  105. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine.egg-info/dependency_links.txt +0 -0
  106. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine.egg-info/requires.txt +0 -0
  107. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/src/render_engine.egg-info/top_level.txt +0 -0
  108. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/conftest.py +0 -0
  109. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_archive.py +0 -0
  110. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_base_object.py +0 -0
  111. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_blog.py +0 -0
  112. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_collections.py +0 -0
  113. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_engine.py +0 -0
  114. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_feeds/conftest_feed.py +0 -0
  115. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_feeds/test_feeds.py +0 -0
  116. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_page.py +0 -0
  117. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_parsers_remove_2024_3_1.py +0 -0
  118. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_plugins.py +0 -0
  119. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_site.py +0 -0
  120. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_site_map.py +0 -0
  121. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_templates/test_base_html.py +0 -0
  122. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/tests/test_theme_manager.py +0 -0
  123. {render_engine-2025.11.1a1 → render_engine-2026.1.1a1}/uv.lock +0 -0
@@ -19,9 +19,9 @@ jobs:
19
19
  build:
20
20
  runs-on: ubuntu-latest
21
21
  steps:
22
- - uses: actions/checkout@v5
22
+ - uses: actions/checkout@v6.0.1
23
23
  - name: Use Node.js 20.x
24
- uses: actions/setup-node@v4
24
+ uses: actions/setup-node@v6
25
25
  with:
26
26
  node-version: 20.x
27
27
  - run: npm install -g @devcontainers/cli
@@ -12,5 +12,5 @@ jobs:
12
12
  labeler:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@v5
15
+ - uses: actions/checkout@v6.0.1
16
16
  - uses: actions/labeler@v5
@@ -13,7 +13,7 @@ jobs:
13
13
  name: Format and Lint Python
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@v5
16
+ - uses: actions/checkout@v6.0.1
17
17
  with:
18
18
  fetch-depth: 0
19
19
  - name: Install Python
@@ -34,7 +34,7 @@ jobs:
34
34
  name: Lint Markdown
35
35
  runs-on: ubuntu-latest
36
36
  steps:
37
- - uses: actions/checkout@v5
37
+ - uses: actions/checkout@v6.0.1
38
38
  - name: Markdown Lint base-files
39
39
  uses: DavidAnson/markdownlint-cli2-action@v20
40
40
  with:
@@ -15,7 +15,7 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
17
  - name: Checkout source
18
- uses: actions/checkout@v5
18
+ uses: actions/checkout@v6.0.1
19
19
 
20
20
  - name: Set up Python 3.11
21
21
  uses: actions/setup-python@v5
@@ -32,7 +32,7 @@ jobs:
32
32
 
33
33
  steps:
34
34
  - name: "Checkout code"
35
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.1
35
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.1.1
36
36
  with:
37
37
  persist-credentials: false
38
38
 
@@ -59,7 +59,7 @@ jobs:
59
59
  # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
60
60
  # format to the repository Actions tab.
61
61
  - name: "Upload artifact"
62
- uses: actions/upload-artifact@v4
62
+ uses: actions/upload-artifact@v6
63
63
  with:
64
64
  name: SARIF
65
65
  path: results.sarif
@@ -68,6 +68,6 @@ jobs:
68
68
  # Upload the results to GitHub's code scanning dashboard (optional).
69
69
  # Commenting out will disable upload of results to your repo's Code Scanning dashboard
70
70
  - name: "Upload to code-scanning"
71
- uses: github/codeql-action/upload-sarif@v3
71
+ uses: github/codeql-action/upload-sarif@v4
72
72
  with:
73
73
  sarif_file: results.sarif
@@ -23,7 +23,7 @@ jobs:
23
23
  python-version: ["3.10", "3.11", "3.12", "3.13"]
24
24
  fail-fast: true
25
25
  steps:
26
- - uses: actions/checkout@v5
26
+ - uses: actions/checkout@v6.0.1
27
27
  - uses: actions/setup-python@v5
28
28
  with:
29
29
  python-version: ${{ matrix.python-version }}
@@ -44,7 +44,7 @@ jobs:
44
44
  fail-fast: true
45
45
  runs-on: ${{ matrix.os }}
46
46
  steps:
47
- - uses: actions/checkout@v5
47
+ - uses: actions/checkout@v6.0.1
48
48
  - uses: actions/setup-python@v5
49
49
  with:
50
50
  python-version: "3.12"
@@ -56,7 +56,7 @@ jobs:
56
56
  run: |
57
57
  python -m pytest
58
58
  - name: coverage
59
- uses: actions/upload-artifact@v4
59
+ uses: actions/upload-artifact@v6
60
60
  with:
61
61
  name: code-coverage-report-${{ matrix.os }}
62
62
  path: .coverage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: render_engine
3
- Version: 2025.11.1a1
3
+ Version: 2026.1.1a1
4
4
  Summary: A Flexible Static Site Generator for Python
5
5
  Project-URL: homepage, https://github.com/render-engine/render-engine/
6
6
  Project-URL: repository, https://github.com/render-engine/render-engine/
@@ -46,7 +46,8 @@ sort_reverse: bool = False
46
46
  title: str
47
47
  template: str | None
48
48
  archive_template str | None: The template to use for the archive pages.
49
- skip_site_map: bool = False: Flag to indicate whether this Collection should not be included in the SiteMap.
49
+ ContentManager: type[ContentManager] | None = FileContentManager: The `ContentManager` to use.
50
+ content_manager_extras: dict[str, Any]: Configuration options to send to the `ContentManager` during instantiation.
50
51
  ```
51
52
 
52
53
  ## Attributes
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: "Enhancing Functionality with Content Managers"
3
+ description: "Guide to using creating Content Managers to use alternate content storage systems."
4
+ date: August 22, 2024
5
+ tags: ["content_manager", "render-engine", "customization"]
6
+ ---
7
+
8
+ Content Managers are a way to control how and where your content is stored.
9
+
10
+ ## Introduction
11
+
12
+ Render Engine `Collection` uses a `ContentManager` to manage the storage of content for site generation. By default
13
+ content is stored in the file system with each piece of content existing in a discrete file. With an alternate
14
+ `ContentManager` the content can be stored in a database, JSON file, or other alternate data store.
15
+
16
+ ## Selecting a `ContentManager`
17
+
18
+ The `ContentManager` for a given [`Collection`](collection.md) is controlled by the `ContentManager` attribute. When
19
+ the class is instantiated the `ContentManager` is also instantiated with any `content_manager_extras` being passed
20
+ as arguments. To access the `ContentManager` of a given `Collection` use the `content_manager` attribute.
21
+
22
+ ## Creating a `ContentManager`
23
+
24
+ To create a `ContentManager` create a sub-class of `ContentManager` that implements the following methods:
25
+
26
+ ```python
27
+ @property
28
+ @abstractmethod
29
+ def pages(self) -> Iterable:
30
+ """The Page objects managed by the content manager"""
31
+ ...
32
+
33
+ @abstractmethod
34
+ def create_entry(self, filepath: Path = None, editor: str = None, metadata: dict = None, content: str = None):
35
+ """Create a new entry"""
36
+ ...
37
+ ```
38
+
39
+ ### `pages`
40
+
41
+ The `pages` property is how the `Collection` accesses its content. It is a method that _must_ be implemented by
42
+ every `ContentManager`. An example `pages` implementation (from the
43
+ [`FileContentManger`](https://github.com/render-engine/render-engine/blob/main/src/render_engine/content_managers/file_content_manager.py)) is:
44
+
45
+ ```python
46
+ @property
47
+ def pages(self) -> Iterable:
48
+ if self._pages is None:
49
+ self._pages = [self.collection.get_page(page) for page in self.iter_content_path()]
50
+ yield from self._pages
51
+ ```
52
+
53
+ ### `create_entry`
54
+
55
+ The `create_entry` method is used by the Render Engine CLI tool to add new entries. It is responsible for adding
56
+ the new entry to the datastore and, if an `editor` is specified, giving the user the ability to edit the new entry
57
+ prior to committing the entry to the datastore.
58
+
59
+ #### Arguments
60
+
61
+ - `filepath: Path`: The path on the filesystem to store the new entry.
62
+ - `editor: str`: The text editor to open for editing the new entry.
63
+ - `content: str`: The initial content for the new entry.
64
+ - `metadata: dict`: The metadata for the new entry.
65
+
66
+ !!! Note
67
+ Not every `ContentManager` actually needs all the arguments that are passed.
@@ -26,6 +26,7 @@ nav:
26
26
  - Site Objects: site.md
27
27
  - Extending Render Engine:
28
28
  - Plugins: plugins.md
29
+ - Content Managers: content_manager.md
29
30
  - Parsers: parsers.md
30
31
  - CLI: cli.md
31
32
  - Templates and Themes:
@@ -4,30 +4,30 @@
4
4
  #
5
5
  # pip-compile --extra=dev --output-file=requirements.txt pyproject.toml
6
6
  #
7
- anyio==4.10.0
7
+ anyio==4.12.0
8
8
  # via
9
9
  # httpx
10
10
  # watchfiles
11
- arrow==1.3.0
11
+ arrow==1.4.0
12
12
  # via cookiecutter
13
13
  babel==2.17.0
14
14
  # via mkdocs-material
15
- backrefs==6.0.1
15
+ backrefs==6.1
16
16
  # via mkdocs-material
17
17
  binaryornot==0.4.4
18
18
  # via cookiecutter
19
- certifi==2025.8.3
19
+ certifi==2025.11.12
20
20
  # via
21
21
  # httpcore
22
22
  # httpx
23
23
  # requests
24
- cfgv==3.4.0
24
+ cfgv==3.5.0
25
25
  # via pre-commit
26
26
  chardet==5.2.0
27
27
  # via binaryornot
28
- charset-normalizer==3.4.3
28
+ charset-normalizer==3.4.4
29
29
  # via requests
30
- click==8.2.1
30
+ click==8.3.1
31
31
  # via
32
32
  # cookiecutter
33
33
  # mkdocs
@@ -38,13 +38,13 @@ colorama==0.4.6
38
38
  # mkdocs-material
39
39
  cookiecutter==2.6.0
40
40
  # via render_engine (pyproject.toml)
41
- coverage[toml]==7.10.6
41
+ coverage[toml]==7.13.1
42
42
  # via pytest-cov
43
43
  distlib==0.4.0
44
44
  # via virtualenv
45
45
  ephemeral-port-reserve==1.1.4
46
46
  # via render_engine (pyproject.toml)
47
- filelock==3.19.1
47
+ filelock==3.20.1
48
48
  # via virtualenv
49
49
  ghp-import==2.1.0
50
50
  # via mkdocs
@@ -52,7 +52,7 @@ gitdb==4.0.12
52
52
  # via gitpython
53
53
  gitpython==3.1.45
54
54
  # via render_engine (pyproject.toml)
55
- griffe==1.13.0
55
+ griffe==1.15.0
56
56
  # via mkdocstrings-python
57
57
  h11==0.16.0
58
58
  # via httpcore
@@ -60,14 +60,14 @@ httpcore==1.0.9
60
60
  # via httpx
61
61
  httpx==0.28.1
62
62
  # via render_engine (pyproject.toml)
63
- identify==2.6.13
63
+ identify==2.6.15
64
64
  # via pre-commit
65
- idna==3.10
65
+ idna==3.11
66
66
  # via
67
67
  # anyio
68
68
  # httpx
69
69
  # requests
70
- iniconfig==2.1.0
70
+ iniconfig==2.3.0
71
71
  # via pytest
72
72
  jinja2==3.1.6
73
73
  # via
@@ -76,7 +76,7 @@ jinja2==3.1.6
76
76
  # mkdocs-material
77
77
  # mkdocstrings
78
78
  # render_engine (pyproject.toml)
79
- markdown==3.8.2
79
+ markdown==3.10
80
80
  # via
81
81
  # mkdocs
82
82
  # mkdocs-autorefs
@@ -89,7 +89,7 @@ markdown2==2.5.4
89
89
  # via
90
90
  # render-engine-markdown
91
91
  # render_engine (pyproject.toml)
92
- markupsafe==3.0.2
92
+ markupsafe==3.0.3
93
93
  # via
94
94
  # jinja2
95
95
  # mkdocs
@@ -113,23 +113,23 @@ mkdocs-autorefs==1.4.3
113
113
  # mkdocstrings-python
114
114
  mkdocs-get-deps==0.2.0
115
115
  # via mkdocs
116
- mkdocs-material==9.6.18
116
+ mkdocs-material==9.7.1
117
117
  # via render_engine (pyproject.toml)
118
118
  mkdocs-material-extensions==1.3.1
119
119
  # via mkdocs-material
120
- mkdocstrings[python]==0.30.0
120
+ mkdocstrings[python]==1.0.0
121
121
  # via
122
122
  # mkdocstrings-python
123
123
  # render_engine (pyproject.toml)
124
- mkdocstrings-python==1.18.2
124
+ mkdocstrings-python==2.0.1
125
125
  # via mkdocstrings
126
126
  more-itertools==10.8.0
127
127
  # via render_engine (pyproject.toml)
128
- mypy==1.17.1
128
+ mypy==1.19.1
129
129
  # via render_engine (pyproject.toml)
130
130
  mypy-extensions==1.1.0
131
131
  # via mypy
132
- nodeenv==1.9.1
132
+ nodeenv==1.10.0
133
133
  # via pre-commit
134
134
  packaging==25.0
135
135
  # via
@@ -139,7 +139,7 @@ paginate==0.5.7
139
139
  # via mkdocs-material
140
140
  pathspec==0.12.1
141
141
  # via mkdocs
142
- platformdirs==4.4.0
142
+ platformdirs==4.5.1
143
143
  # via
144
144
  # mkdocs-get-deps
145
145
  # virtualenv
@@ -147,25 +147,25 @@ pluggy==1.6.0
147
147
  # via
148
148
  # pytest
149
149
  # render_engine (pyproject.toml)
150
- pre-commit==4.3.0
150
+ pre-commit==4.5.1
151
151
  # via render_engine (pyproject.toml)
152
152
  pygments==2.19.2
153
153
  # via
154
154
  # mkdocs-material
155
155
  # rich
156
- pymdown-extensions==10.16.1
156
+ pymdown-extensions==10.20
157
157
  # via
158
158
  # mkdocs-material
159
159
  # mkdocstrings
160
160
  # render_engine (pyproject.toml)
161
- pytest==8.4.2
161
+ pytest==9.0.2
162
162
  # via
163
163
  # pytest-cov
164
164
  # pytest-mock
165
165
  # render_engine (pyproject.toml)
166
- pytest-cov==6.2.1
166
+ pytest-cov==7.0.0
167
167
  # via render_engine (pyproject.toml)
168
- pytest-mock==3.14.1
168
+ pytest-mock==3.15.1
169
169
  # via render_engine (pyproject.toml)
170
170
  python-dateutil==2.9.0.post0
171
171
  # via
@@ -180,7 +180,7 @@ python-slugify==8.0.4
180
180
  # via
181
181
  # cookiecutter
182
182
  # render_engine (pyproject.toml)
183
- pyyaml==6.0.2
183
+ pyyaml==6.0.3
184
184
  # via
185
185
  # cookiecutter
186
186
  # mkdocs
@@ -201,12 +201,12 @@ requests==2.32.5
201
201
  # via
202
202
  # cookiecutter
203
203
  # mkdocs-material
204
- rich==14.1.0
204
+ rich==14.2.0
205
205
  # via
206
206
  # cookiecutter
207
207
  # render_engine (pyproject.toml)
208
208
  # typer
209
- ruff==0.12.11
209
+ ruff==0.14.10
210
210
  # via render_engine (pyproject.toml)
211
211
  shellingham==1.5.4
212
212
  # via typer
@@ -219,22 +219,22 @@ sniffio==1.3.1
219
219
  text-unidecode==1.3
220
220
  # via python-slugify
221
221
  toml==0.10.2
222
- typer==0.17.3
222
+ typer==0.21.0
223
223
  # via render_engine (pyproject.toml)
224
- types-python-dateutil==2.9.0.20250822
224
+ types-python-dateutil==2.9.0.20251115
225
225
  # via arrow
226
226
  typing-extensions==4.15.0
227
227
  # via
228
228
  # anyio
229
229
  # mypy
230
230
  # typer
231
- tzdata==2025.2
231
+ tzdata==2025.3
232
232
  # via render_engine (pyproject.toml)
233
- urllib3==2.5.0
233
+ urllib3==2.6.2
234
234
  # via requests
235
- virtualenv==20.34.0
235
+ virtualenv==20.35.4
236
236
  # via pre-commit
237
237
  watchdog==6.0.0
238
238
  # via mkdocs
239
- watchfiles==1.1.0
239
+ watchfiles==1.1.1
240
240
  # via render_engine (pyproject.toml)
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '2025.11.1a1'
32
- __version_tuple__ = version_tuple = (2025, 11, 1, 'a1')
31
+ __version__ = version = '2026.1.1a1'
32
+ __version_tuple__ = version_tuple = (2026, 1, 1, 'a1')
33
33
 
34
- __commit_id__ = commit_id = 'g5ed5705d6'
34
+ __commit_id__ = commit_id = 'gb307476d1'
@@ -27,6 +27,7 @@ class BaseObject:
27
27
  plugins: list[Callable] | None
28
28
  plugin_settings: dict = {"plugins": defaultdict(dict)}
29
29
  skip_site_map: bool = False
30
+ metadata: dict = dict()
30
31
 
31
32
  @property
32
33
  def _title(self) -> str:
@@ -1,7 +1,9 @@
1
1
  import copy
2
2
  import datetime
3
3
  import logging
4
+ import os
4
5
  from collections.abc import Callable, Generator
6
+ from multiprocessing.pool import ThreadPool
5
7
  from pathlib import Path
6
8
  from typing import Any
7
9
 
@@ -245,6 +247,25 @@ class Collection(BaseObject):
245
247
  def __iter__(self):
246
248
  yield from self.content_manager
247
249
 
250
+ @property
251
+ def all_content(self) -> Generator:
252
+ """All of the content that is associated with the Collection including Pages, Feed, and Archives"""
253
+ yield from self
254
+
255
+ if getattr(self, "has_archive", False):
256
+ for archive in self.archives:
257
+ yield archive
258
+ if archive.is_index:
259
+ # In order to avoid collision with parallel processing we need to do a copy.
260
+ # A deepcopy is not necessary because we only care about not overwriting the
261
+ # slug on the original.
262
+ index = copy.copy(archive)
263
+ index.slug = "index"
264
+ yield index
265
+
266
+ if feed := getattr(self, "feed", None):
267
+ yield feed
268
+
248
269
  def _run_collection_plugins(self, site, hook_type: str):
249
270
  """
250
271
  Run plugins for a collection
@@ -261,32 +282,29 @@ class Collection(BaseObject):
261
282
  return
262
283
  method(collection=self, site=site, settings=self.plugin_manager.plugin_settings)
263
284
 
264
- def render(self) -> None:
265
- """Iterate through Pages and Check for Collections and Feeds"""
285
+ def _render(self, entry):
286
+ """
287
+ Renders 1 entry in the Collection
266
288
 
267
- for entry in self:
289
+ :param entry: The entry to process
290
+ """
291
+ if not isinstance(entry, RSSFeed) and not isinstance(entry, Archive):
268
292
  entry.plugin_manager = copy.deepcopy(self.plugin_manager)
269
293
 
270
- for route in entry.routes:
271
- entry.site = self.site
272
- entry.render(route, self.site.theme_manager)
273
-
274
- if getattr(self, "has_archive", False):
275
- for archive in self.archives:
276
- archive.site = self.site
277
- logging.debug("Adding Archive: %s", archive.__class__.__name__)
278
-
279
- for _ in self.routes:
280
- archive.render(self.routes[0], self.site.theme_manager)
294
+ entry.site = self.site
295
+ for route in entry.routes:
296
+ entry.render(route, self.site.theme_manager)
281
297
 
282
- if archive.is_index:
283
- archive.slug = "index"
284
- archive.render(self.routes[0], self.site.theme_manager)
285
- feed: RSSFeed
286
- if hasattr(self, "Feed"):
287
- feed = self.feed
288
- feed.site = self.site
289
- feed.render(route="./", theme_manager=self.site.theme_manager)
298
+ def render(self) -> None:
299
+ """Iterate through Pages and Check for Archives and Feeds"""
300
+
301
+ # Use a ThreadPool to process all of the entries in the collection in parallel.
302
+ # This is limited to the number of CPUs available. The easiest way to implement
303
+ # this parallelization for a single task is to use the imap_unordered method of
304
+ # the ThreadPool. This is, effectively, a generator so we need to loop over it
305
+ # for them to run. Since there is no actual return value to look at we just `pass`.
306
+ for entry in ThreadPool(processes=os.cpu_count()).imap_unordered(self._render, self.all_content):
307
+ pass
290
308
 
291
309
  def create_entry(
292
310
  self, filepath: Path = None, editor: str = None, content: str = None, metadata: dict = None
@@ -204,10 +204,10 @@ class Page(BasePage):
204
204
 
205
205
  content: Any
206
206
  content_path: Path | str | None
207
- Parser: type[BasePageParser] = BasePageParser
208
207
  inherit_plugins: bool
209
208
  parser_extras: dict[str, Any] | None
210
209
  title: str
210
+ Parser: type[BasePageParser] = BasePageParser
211
211
 
212
212
  def __init__(
213
213
  self,
@@ -229,17 +229,17 @@ class Page(BasePage):
229
229
 
230
230
  # Parse Content from the Content Path or the Content
231
231
  if content_path := (content_path or getattr(self, "content_path", None)):
232
- attrs, self.content = self.Parser.parse_content_path(content_path)
232
+ self.metadata, self.content = self.Parser.parse_content_path(content_path)
233
233
 
234
234
  elif content := (content or getattr(self, "content", None)):
235
- attrs, self.content = self.Parser.parse_content(content)
235
+ self.metadata, self.content = self.Parser.parse_content(content)
236
236
 
237
237
  else:
238
- attrs = {}
238
+ self.metadata = {}
239
239
  self.content = None
240
240
 
241
241
  # Set the attributes
242
- for key, val in attrs.items():
242
+ for key, val in self.metadata.items():
243
243
  setattr(self, key.lower(), val)
244
244
 
245
245
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: render_engine
3
- Version: 2025.11.1a1
3
+ Version: 2026.1.1a1
4
4
  Summary: A Flexible Static Site Generator for Python
5
5
  Project-URL: homepage, https://github.com/render-engine/render-engine/
6
6
  Project-URL: repository, https://github.com/render-engine/render-engine/
@@ -34,6 +34,7 @@ docs/requirements.txt
34
34
  docs/docs/archive.md
35
35
  docs/docs/cli.md
36
36
  docs/docs/collection.md
37
+ docs/docs/content_manager.md
37
38
  docs/docs/custom_collections.md
38
39
  docs/docs/feeds.md
39
40
  docs/docs/index.md