hyperflask 0.4.3__tar.gz → 0.5.0__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 (122) hide show
  1. {hyperflask-0.4.3 → hyperflask-0.5.0}/PKG-INFO +4 -3
  2. {hyperflask-0.4.3 → hyperflask-0.5.0}/pyproject.toml +4 -3
  3. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/__init__.py +1 -0
  4. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/app.py +20 -26
  5. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/cli/__init__.py +3 -7
  6. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/components/core.py +2 -0
  7. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/components/jsx.py +2 -2
  8. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/components/webcomponent.py +2 -2
  9. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/layouts/web.html +1 -1
  10. hyperflask-0.5.0/src/hyperflask/utils/freezer.py +56 -0
  11. hyperflask-0.5.0/src/hyperflask/utils/image.py +26 -0
  12. {hyperflask-0.4.3 → hyperflask-0.5.0}/README.md +0 -0
  13. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/__main__.py +0 -0
  14. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/actors.py +0 -0
  15. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/cli/reloader.py +0 -0
  16. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/cli/runner.py +0 -0
  17. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/cli/worker.py +0 -0
  18. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/components/__init__.py +0 -0
  19. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/components/alpine.py +0 -0
  20. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/factory.py +0 -0
  21. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/forms/__init__.py +0 -0
  22. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/forms/fields.py +0 -0
  23. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/forms/widgets.py +0 -0
  24. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/layouts/email.mjml +0 -0
  25. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/model.py +0 -0
  26. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/page_helpers.py +0 -0
  27. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/security.py +0 -0
  28. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/alpine-csp.js +0 -0
  29. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/alpine.js +0 -0
  30. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/app.css +0 -0
  31. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/app.js +0 -0
  32. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/htmx-exts.js +0 -0
  33. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/react.js +0 -0
  34. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/stimulus.js +0 -0
  35. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/static/translations.js +0 -0
  36. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Accordion.html +0 -0
  37. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/AccordionPanel.html +0 -0
  38. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Alert.html +0 -0
  39. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Avatar.html +0 -0
  40. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/AvatarGroup.html +0 -0
  41. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Badge.html +0 -0
  42. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Breadcrumbs.html +0 -0
  43. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/BreadcrumbsItem.html +0 -0
  44. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Button.html +0 -0
  45. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Card.html +0 -0
  46. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/CardActions.html +0 -0
  47. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/CardTitle.html +0 -0
  48. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/ChatBubble.html +0 -0
  49. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Checkbox.html +0 -0
  50. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/CollapsibleSubMenu.html +0 -0
  51. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Countdown.html +0 -0
  52. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Divider.html +0 -0
  53. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Drawer.html +0 -0
  54. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/DrawerContent.html +0 -0
  55. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/DrawerSide.html +0 -0
  56. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Dropdown.html +0 -0
  57. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/DropdownItem.html +0 -0
  58. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Fieldset.html +0 -0
  59. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/FieldsetLegend.html +0 -0
  60. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/FileInput.html +0 -0
  61. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Filter.html +0 -0
  62. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/FilterItem.html +0 -0
  63. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Footer.html +0 -0
  64. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/FooterLink.html +0 -0
  65. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/FooterNav.html +0 -0
  66. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Indicator.html +0 -0
  67. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Input.html +0 -0
  68. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/InputLabel.html +0 -0
  69. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Join.html +0 -0
  70. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Kbd.html +0 -0
  71. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Label.html +0 -0
  72. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/LabeledCheckbox.html +0 -0
  73. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/LabeledToggle.html +0 -0
  74. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Link.html +0 -0
  75. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/List.html +0 -0
  76. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/ListItem.html +0 -0
  77. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Loading.html +0 -0
  78. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Menu.html +0 -0
  79. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/MenuItem.html +0 -0
  80. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/MenuTitle.html +0 -0
  81. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Modal.html +0 -0
  82. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/ModalAction.html +0 -0
  83. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/ModalTitle.html +0 -0
  84. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Navbar.html +0 -0
  85. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Pagination.html +0 -0
  86. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/PaginationNext.html +0 -0
  87. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/PaginationPage.html +0 -0
  88. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/PaginationPrev.html +0 -0
  89. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Progress.html +0 -0
  90. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/README.md +0 -0
  91. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Radio.html +0 -0
  92. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Range.html +0 -0
  93. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Rating.html +0 -0
  94. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Select.html +0 -0
  95. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Stack.html +0 -0
  96. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Stat.html +0 -0
  97. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Stats.html +0 -0
  98. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Step.html +0 -0
  99. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Steps.html +0 -0
  100. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/SubMenu.html +0 -0
  101. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Swap.html +0 -0
  102. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Tab.html +0 -0
  103. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Table.html +0 -0
  104. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Tabs.html +0 -0
  105. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Textarea.html +0 -0
  106. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Toast.html +0 -0
  107. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Toggle.html +0 -0
  108. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/daisyui/Tooltip.html +0 -0
  109. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/flash.html +0 -0
  110. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/forms.html +0 -0
  111. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/icons.html +0 -0
  112. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/layout.html +0 -0
  113. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/modal.html +0 -0
  114. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/skeleton.html +0 -0
  115. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/ui/sse.html +0 -0
  116. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/utils/__init__.py +0 -0
  117. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/utils/html.py +0 -0
  118. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/utils/htmx.py +0 -0
  119. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/utils/markdown.py +0 -0
  120. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/utils/metadata.py +0 -0
  121. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/utils/page_actions.py +0 -0
  122. {hyperflask-0.4.3 → hyperflask-0.5.0}/src/hyperflask/utils/tokens.py +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperflask
3
- Version: 0.4.3
3
+ Version: 0.5.0
4
4
  Summary: Full stack web framework
5
5
  Author: Maxime Bouroumeau-Fuseau
6
6
  Author-email: Maxime Bouroumeau-Fuseau <maxime.bouroumeau@gmail.com>
7
7
  License-Expression: MIT
8
8
  Requires-Dist: flask>=3.0.0,<4.0.0
9
- Requires-Dist: flask-assets-pipeline~=0.7
9
+ Requires-Dist: flask-assets-pipeline~=0.8
10
10
  Requires-Dist: flask-babel-hyper~=5.1
11
- Requires-Dist: flask-collections~=0.2
11
+ Requires-Dist: flask-collections~=0.3
12
12
  Requires-Dist: flask-configurator~=0.1
13
13
  Requires-Dist: flask-debugtoolbar~=0.16
14
14
  Requires-Dist: flask-file-routes~=0.3
@@ -32,6 +32,7 @@ Requires-Dist: jinja-wtforms~=0.2
32
32
  Requires-Dist: periodiq~=0.13
33
33
  Requires-Dist: python-slugify~=8.0.4
34
34
  Requires-Dist: watchdog>=6.0.0
35
+ Requires-Dist: pillow>=11.3.0
35
36
  Requires-Python: >=3.10
36
37
  Project-URL: Repository, https://github.com/hyperflask/hyperflask
37
38
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperflask"
3
- version = "0.4.3"
3
+ version = "0.5.0"
4
4
  description = "Full stack web framework"
5
5
  authors = [
6
6
  {"name" = "Maxime Bouroumeau-Fuseau", email = "maxime.bouroumeau@gmail.com"}
@@ -10,9 +10,9 @@ license = "MIT"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  "flask>=3.0.0,<4.0.0",
13
- "flask-assets-pipeline ~=0.7",
13
+ "flask-assets-pipeline ~=0.8",
14
14
  "flask-babel-hyper ~=5.1",
15
- "flask-collections ~=0.2",
15
+ "flask-collections ~=0.3",
16
16
  "flask-configurator ~=0.1",
17
17
  "flask-debugtoolbar ~=0.16",
18
18
  "flask-file-routes ~=0.3",
@@ -37,6 +37,7 @@ dependencies = [
37
37
  "periodiq ~=0.13",
38
38
  "python-slugify ~=8.0.4",
39
39
  "watchdog>=6.0.0",
40
+ "pillow>=11.3.0",
40
41
  ]
41
42
 
42
43
  [tool.uv.sources]
@@ -1,6 +1,7 @@
1
1
  from flask import *
2
2
  from .app import Hyperflask
3
3
  from .utils.htmx import htmx_redirect, htmx_oob
4
+ from .utils.freezer import StaticMode, static, dynamic
4
5
  from .security import csp_nonce
5
6
 
6
7
  # useful exports from hyperflask managed extensions
@@ -20,7 +20,6 @@ from flask_suspense import Suspense, render_template, suspense_before_render_tem
20
20
  #from flask_observability import Observability
21
21
  # 3rd party extensions
22
22
  from flask_wtf.csrf import CSRFProtect
23
- from flask_frozen import Freezer
24
23
  from htmx_flask import Htmx
25
24
  from flask_mailman import Mail
26
25
  from flask_debugtoolbar import DebugToolbarExtension
@@ -31,6 +30,8 @@ from .utils.htmx import htmx_oob, respond_with_flash_messages
31
30
  from .utils.markdown import jinja_markdown, MarkdownExtension
32
31
  from .utils.html import sanitize_html, nl2br
33
32
  from .utils.metadata import metadata_tags
33
+ from .utils.freezer import StaticMode, Freezer, freezer_url_generator
34
+ from .utils.image import image_tag
34
35
  from .components import register_components
35
36
  from .model import Model, File as SQLFileType, UndefinedDatabase
36
37
  from .actors import AppContextMiddleware, discover_broker, make_actor_decorator
@@ -41,7 +42,6 @@ from jinja_super_macros.registry import FileLoader
41
42
  from jinja_wtforms.extractor import map_jinja_call_node_to_func
42
43
  from sqlorm.sql_template import SQLTemplate
43
44
  from periodiq import PeriodiqMiddleware
44
- import dramatiq
45
45
  import os
46
46
 
47
47
 
@@ -55,7 +55,7 @@ map_jinja_call_node_to_func(lazy_gettext, "lazy_gettext")
55
55
  class Hyperflask(Flask):
56
56
  config_class = Config
57
57
 
58
- def __init__(self, *args, static_mode="hybrid", instrument=False, layouts_folder="layouts",
58
+ def __init__(self, *args, static_mode=StaticMode.DYNAMIC, instrument=False, layouts_folder="layouts",
59
59
  emails_folder="emails", forms_folder="forms", assets_folder="assets", pages_folder="pages", migrations_folder="migrations",
60
60
  config=None, config_filename="config.yml", database_uri=None, proxy_fix=True, **kwargs):
61
61
 
@@ -64,8 +64,12 @@ class Hyperflask(Flask):
64
64
  self.wsgi_app = ProxyFix(self.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)
65
65
 
66
66
  self.config.update({
67
- "FREEZER_DESTINATION": os.path.join(self.root_path, "_site"),
67
+ "FREEZER_DESTINATION": "_site",
68
68
  "FREEZER_STATIC_IGNORE": [],
69
+ "FREEZER_IGNORE_404_NOT_FOUND": True,
70
+ "FREEZER_IGNORE_MIMETYPE_WARNINGS": True,
71
+ "FREEZER_REDIRECT_POLICY": "ignore",
72
+ "FREEZER_DEFAULT_FILE_EXTENSION": "html",
69
73
  "WTF_CSRF_CHECK_DEFAULT": False,
70
74
  "OTEL_INSTRUMENT": instrument,
71
75
  "DEBUG_TB_INTERCEPT_REDIRECTS": False,
@@ -92,7 +96,9 @@ class Hyperflask(Flask):
92
96
  "CSP_FRAME_ANCESTORS": True,
93
97
  "CSP_FRAME_ANCESTORS_SAFE_ENDPOINTS": [],
94
98
  "REFERRER_POLICY": "strict-origin-when-cross-origin",
95
- "HSTS_HEADER": False
99
+ "HSTS_HEADER": False,
100
+ "IMAGES_DEFAULT_LOADING": "lazy",
101
+ "IMAGES_DEFAULT_DECODING": "async",
96
102
  })
97
103
  if config:
98
104
  self.config.update(config)
@@ -123,8 +129,13 @@ class Hyperflask(Flask):
123
129
  self.jinja_env.add_extension(LayoutExtension)
124
130
  self.jinja_env.add_extension(WtformExtension)
125
131
  self.jinja_env.add_extension(MarkdownExtension)
126
- self.jinja_env.filters.update(markdown=jinja_markdown, sanitize=sanitize_html, nl2br=nl2br)
127
- self.jinja_env.globals.update(page_action_url=page_action_url, app=self, csp_nonce=csp_nonce, metadata_tags=metadata_tags)
132
+ self.jinja_env.filters.update(markdown=jinja_markdown,
133
+ sanitize=sanitize_html,
134
+ nl2br=nl2br)
135
+ self.jinja_env.globals.update(page_action_url=page_action_url,
136
+ app=self,
137
+ csp_nonce=csp_nonce,
138
+ metadata_tags=metadata_tags)
128
139
  self.jinja_env.form_base_cls = Form
129
140
  self.jinja_env.default_layout = self.config["LAYOUT"]
130
141
  self.forms = self.jinja_env.forms
@@ -140,7 +151,7 @@ class Hyperflask(Flask):
140
151
  ])
141
152
 
142
153
  DebugToolbarExtension(self)
143
- self.freezer = Freezer(self, with_no_argument_rules=False)
154
+ self.freezer = Freezer(self, with_no_argument_rules=False, log_url_for=self.config.get('FREEZER_LOG_URL_FOR', False))
144
155
  self.freezer.register_generator(lambda: freezer_url_generator(self))
145
156
  self.csrf = CSRFProtect(self)
146
157
  Htmx(self)
@@ -194,6 +205,7 @@ class Hyperflask(Flask):
194
205
  if hasattr(file_routes, "loader"):
195
206
  self.forms.register_from_loader(file_routes.loader, "pages")
196
207
  self.macros.create_from_func(htmx_oob, "HtmxOob", receive_caller=True, caller_alias="html")
208
+ self.macros.create_from_func(image_tag, "Image")
197
209
 
198
210
  self.components_loader, _ = register_components(self)
199
211
  if self.components_loader:
@@ -286,21 +298,3 @@ class HyperModuleView(ModuleView):
286
298
 
287
299
  def _render_template(self):
288
300
  return render_template(page.template)
289
-
290
-
291
- def static(func):
292
- pass
293
-
294
-
295
- def dynamic(func=None, static_template=None):
296
- def decorator(func):
297
- pass
298
- return decorator(func) if func else decorator
299
-
300
-
301
- def freezer_url_generator(app):
302
- """URL generator for URL rules that take no arguments."""
303
- for rule in app.url_map.iter_rules():
304
- if not rule.arguments and 'GET' in rule.methods and not rule.endpoint.startswith('mercure'):
305
- app.logger.info(rule.endpoint)
306
- yield rule.endpoint, {}
@@ -4,6 +4,7 @@ from flask_assets_pipeline import cli as assets_cli
4
4
  import click
5
5
  from ..factory import create_app
6
6
  from ..security import generate_csp_policy
7
+ from ..utils.freezer import StaticMode
7
8
  from .runner import serve_command, run_command, dev_command
8
9
  from .worker import worker_command, scheduler_command
9
10
 
@@ -31,16 +32,12 @@ def init_command():
31
32
  pass
32
33
 
33
34
 
34
- @cli.command("freeze")
35
- def freeze_command():
36
- current_app.freezer.freeze()
37
-
38
-
39
35
  @cli.command("build")
40
36
  @click.pass_context
41
37
  def build_command(ctx):
42
38
  ctx.invoke(assets_cli.build)
43
- ctx.invoke(freeze_command)
39
+ if StaticMode(current_app.config['STATIC_MODE']) != StaticMode.DYNAMIC:
40
+ current_app.freezer.freeze()
44
41
 
45
42
 
46
43
  @cli.command("csp-header")
@@ -48,6 +45,5 @@ def csp_header_command():
48
45
  click.echo(generate_csp_policy())
49
46
 
50
47
 
51
-
52
48
  def main():
53
49
  cli.main()
@@ -4,6 +4,7 @@ from flask.views import http_method_funcs
4
4
  from flask_file_routes import page
5
5
  from flask_suspense import render_template
6
6
  from jinjapy import extract_frontmatter
7
+ from ..utils.freezer import dynamic
7
8
  import importlib
8
9
  import re
9
10
  import os
@@ -43,6 +44,7 @@ class ComponentAdapter(BaseComponentAdapter):
43
44
  if methods:
44
45
  app.add_url_rule(url, self.name, view_func=self.view_func, methods=methods)
45
46
 
47
+ @dynamic
46
48
  def view_func(self):
47
49
  props = {}
48
50
  page.template = self.template
@@ -1,7 +1,7 @@
1
1
  from .core import BaseComponentAdapter
2
2
  from markupsafe import Markup
3
3
  from flask import current_app
4
- from flask_assets_pipeline import Entrypoint
4
+ from flask_assets_pipeline import BundleEntrypoint
5
5
  import json
6
6
  import html
7
7
 
@@ -13,7 +13,7 @@ class JsxComponentAdapter(BaseComponentAdapter):
13
13
  app.assets.state.bundles["@components"] = []
14
14
  app.assets.include("@components")
15
15
  app.assets.state.bundles["@components"].append(
16
- Entrypoint.create(self.template, from_package="jinja"))
16
+ BundleEntrypoint.create(self.template, from_package="jinja"))
17
17
  app.assets.include(self.include_js)
18
18
 
19
19
  def render(self, caller, *args, **kwargs):
@@ -1,6 +1,6 @@
1
1
  from .core import BaseComponentAdapter
2
2
  from jinja_super_macros import html_tag
3
- from flask_assets_pipeline import Entrypoint
3
+ from flask_assets_pipeline import BundleEntrypoint
4
4
  from markupsafe import Markup
5
5
  import os
6
6
 
@@ -17,7 +17,7 @@ class WebComponentAdapter(BaseComponentAdapter):
17
17
  app.assets.state.bundles["@components"] = []
18
18
  app.assets.include("@components")
19
19
  app.assets.state.bundles["@components"].append(
20
- Entrypoint.create(os.path.abspath(tpl.filename), self.template))
20
+ BundleEntrypoint.create(os.path.abspath(tpl.filename), self.template))
21
21
 
22
22
  def render(self, caller, *args, **kwargs):
23
23
  tag_name = self.name.replace("_", "-")
@@ -5,7 +5,7 @@
5
5
  {% block head_metas %}
6
6
  {{ metadata_tags(page.get('metadata', {})) }}
7
7
  {% endblock %}
8
- {% asset_tags nonce=csp_nonce %}
8
+ {% asset_tags(nonce=csp_nonce) %}
9
9
  {% block head_assets %}{% endblock %}
10
10
  {%- endblock %}
11
11
  </head>
@@ -0,0 +1,56 @@
1
+ from flask_frozen import Freezer as FlaskFreezer
2
+ from pathlib import Path
3
+ from enum import Enum
4
+
5
+
6
+ class StaticMode(Enum):
7
+ STATIC = "static"
8
+ HYBRID = "hybrid"
9
+ DYNAMIC = "dynamic"
10
+
11
+
12
+ class Freezer(FlaskFreezer):
13
+ @property
14
+ def root(self):
15
+ return Path(self.app.config['FREEZER_DESTINATION'])
16
+
17
+ def _check_endpoints(self, seen_endpoints):
18
+ if StaticMode(self.app.config['STATIC_MODE']) != StaticMode.STATIC:
19
+ return
20
+ super()._check_endpoints(seen_endpoints)
21
+
22
+ def urlpath_to_filepath(self, path):
23
+ """Convert URL path like /admin/ to file path like admin/index.html."""
24
+ if path.endswith('/'):
25
+ path += 'index.html'
26
+ # Remove the initial slash that should always be there
27
+ assert path.startswith('/')
28
+ if self.app.config.get('FREEZER_DEFAULT_FILE_EXTENSION') and '.' not in path.split('/')[-1]:
29
+ path += f".{self.app.config['FREEZER_DEFAULT_FILE_EXTENSION']}"
30
+ return path[1:]
31
+
32
+
33
+ def static(func=None, values=None):
34
+ if func is None:
35
+ return lambda f: static(f, values=values)
36
+ func.__freezer__ = True
37
+ func.__freezer_values__ = values
38
+ return func
39
+
40
+
41
+ def dynamic(func):
42
+ func.__freezer__ = False
43
+ return func
44
+
45
+
46
+ def freezer_url_generator(app):
47
+ """URL generator for URL rules that take no arguments."""
48
+ for rule in app.url_map.iter_rules():
49
+ if rule.arguments or 'GET' not in rule.methods:
50
+ continue
51
+ if rule.endpoint.startswith('mercure_hub'):
52
+ continue
53
+ if rule.endpoint in app.view_functions and not getattr(app.view_functions[rule.endpoint], '__freezer__', True):
54
+ continue
55
+ values = getattr(app.view_functions.get(rule.endpoint), '__freezer_values__', None) or {}
56
+ yield rule.endpoint, values
@@ -0,0 +1,26 @@
1
+ from flask import current_app
2
+ from jinja_super_macros import html_tag
3
+ from flask_assets_pipeline import image_attrs
4
+
5
+
6
+ def image_tag(src, priority=False, preload=False, placeholder=True, loading=None, decoding=None, **attrs):
7
+ if not loading:
8
+ loading = current_app.config["IMAGES_DEFAULT_LOADING"]
9
+ if not decoding:
10
+ decoding = current_app.config["IMAGES_DEFAULT_DECODING"]
11
+ if priority:
12
+ preload = True
13
+ attrs.setdefault("fetchpriority", "high")
14
+ if preload:
15
+ current_app.assets.include([(src, {"modifier": "preload", "content_type": "image"})])
16
+ if loading == "lazy":
17
+ loading = "eager"
18
+ _attrs = image_attrs(src)
19
+ if not placeholder and "style" in _attrs:
20
+ del _attrs["style"]
21
+ if loading:
22
+ _attrs["loading"] = loading
23
+ if decoding:
24
+ _attrs["decoding"] = decoding
25
+ _attrs.update(attrs)
26
+ return html_tag("img", **_attrs)
File without changes