hyperflask 0.5.0__tar.gz → 0.5.2__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. {hyperflask-0.5.0 → hyperflask-0.5.2}/PKG-INFO +1 -1
  2. {hyperflask-0.5.0 → hyperflask-0.5.2}/pyproject.toml +1 -1
  3. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/app.py +13 -1
  4. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/cli/__init__.py +2 -5
  5. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/cli/runner.py +45 -35
  6. hyperflask-0.5.2/src/hyperflask/cli/scaffold.py +32 -0
  7. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/components/core.py +2 -2
  8. {hyperflask-0.5.0 → hyperflask-0.5.2}/README.md +0 -0
  9. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/__init__.py +0 -0
  10. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/__main__.py +0 -0
  11. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/actors.py +0 -0
  12. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/cli/reloader.py +0 -0
  13. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/cli/worker.py +0 -0
  14. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/components/__init__.py +0 -0
  15. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/components/alpine.py +0 -0
  16. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/components/jsx.py +0 -0
  17. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/components/webcomponent.py +0 -0
  18. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/factory.py +0 -0
  19. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/forms/__init__.py +0 -0
  20. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/forms/fields.py +0 -0
  21. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/forms/widgets.py +0 -0
  22. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/layouts/email.mjml +0 -0
  23. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/layouts/web.html +0 -0
  24. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/model.py +0 -0
  25. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/page_helpers.py +0 -0
  26. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/security.py +0 -0
  27. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/alpine-csp.js +0 -0
  28. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/alpine.js +0 -0
  29. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/app.css +0 -0
  30. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/app.js +0 -0
  31. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/htmx-exts.js +0 -0
  32. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/react.js +0 -0
  33. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/stimulus.js +0 -0
  34. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/static/translations.js +0 -0
  35. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Accordion.html +0 -0
  36. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/AccordionPanel.html +0 -0
  37. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Alert.html +0 -0
  38. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Avatar.html +0 -0
  39. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/AvatarGroup.html +0 -0
  40. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Badge.html +0 -0
  41. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Breadcrumbs.html +0 -0
  42. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/BreadcrumbsItem.html +0 -0
  43. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Button.html +0 -0
  44. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Card.html +0 -0
  45. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/CardActions.html +0 -0
  46. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/CardTitle.html +0 -0
  47. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/ChatBubble.html +0 -0
  48. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Checkbox.html +0 -0
  49. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/CollapsibleSubMenu.html +0 -0
  50. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Countdown.html +0 -0
  51. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Divider.html +0 -0
  52. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Drawer.html +0 -0
  53. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/DrawerContent.html +0 -0
  54. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/DrawerSide.html +0 -0
  55. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Dropdown.html +0 -0
  56. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/DropdownItem.html +0 -0
  57. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Fieldset.html +0 -0
  58. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/FieldsetLegend.html +0 -0
  59. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/FileInput.html +0 -0
  60. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Filter.html +0 -0
  61. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/FilterItem.html +0 -0
  62. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Footer.html +0 -0
  63. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/FooterLink.html +0 -0
  64. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/FooterNav.html +0 -0
  65. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Indicator.html +0 -0
  66. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Input.html +0 -0
  67. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/InputLabel.html +0 -0
  68. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Join.html +0 -0
  69. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Kbd.html +0 -0
  70. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Label.html +0 -0
  71. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/LabeledCheckbox.html +0 -0
  72. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/LabeledToggle.html +0 -0
  73. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Link.html +0 -0
  74. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/List.html +0 -0
  75. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/ListItem.html +0 -0
  76. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Loading.html +0 -0
  77. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Menu.html +0 -0
  78. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/MenuItem.html +0 -0
  79. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/MenuTitle.html +0 -0
  80. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Modal.html +0 -0
  81. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/ModalAction.html +0 -0
  82. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/ModalTitle.html +0 -0
  83. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Navbar.html +0 -0
  84. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Pagination.html +0 -0
  85. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/PaginationNext.html +0 -0
  86. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/PaginationPage.html +0 -0
  87. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/PaginationPrev.html +0 -0
  88. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Progress.html +0 -0
  89. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/README.md +0 -0
  90. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Radio.html +0 -0
  91. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Range.html +0 -0
  92. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Rating.html +0 -0
  93. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Select.html +0 -0
  94. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Stack.html +0 -0
  95. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Stat.html +0 -0
  96. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Stats.html +0 -0
  97. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Step.html +0 -0
  98. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Steps.html +0 -0
  99. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/SubMenu.html +0 -0
  100. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Swap.html +0 -0
  101. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Tab.html +0 -0
  102. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Table.html +0 -0
  103. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Tabs.html +0 -0
  104. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Textarea.html +0 -0
  105. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Toast.html +0 -0
  106. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Toggle.html +0 -0
  107. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/daisyui/Tooltip.html +0 -0
  108. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/flash.html +0 -0
  109. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/forms.html +0 -0
  110. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/icons.html +0 -0
  111. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/layout.html +0 -0
  112. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/modal.html +0 -0
  113. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/skeleton.html +0 -0
  114. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/ui/sse.html +0 -0
  115. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/__init__.py +0 -0
  116. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/freezer.py +0 -0
  117. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/html.py +0 -0
  118. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/htmx.py +0 -0
  119. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/image.py +0 -0
  120. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/markdown.py +0 -0
  121. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/metadata.py +0 -0
  122. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/page_actions.py +0 -0
  123. {hyperflask-0.5.0 → hyperflask-0.5.2}/src/hyperflask/utils/tokens.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperflask
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Full stack web framework
5
5
  Author: Maxime Bouroumeau-Fuseau
6
6
  Author-email: Maxime Bouroumeau-Fuseau <maxime.bouroumeau@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperflask"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "Full stack web framework"
5
5
  authors = [
6
6
  {"name" = "Maxime Bouroumeau-Fuseau", email = "maxime.bouroumeau@gmail.com"}
@@ -57,7 +57,7 @@ class Hyperflask(Flask):
57
57
 
58
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
- config=None, config_filename="config.yml", database_uri=None, proxy_fix=True, **kwargs):
60
+ config=None, config_filename="config.yml", database_uri=None, proxy_fix=True, healthcheck_url="/healthcheck", **kwargs):
61
61
 
62
62
  super().__init__(*args, **kwargs)
63
63
  if proxy_fix:
@@ -99,6 +99,7 @@ class Hyperflask(Flask):
99
99
  "HSTS_HEADER": False,
100
100
  "IMAGES_DEFAULT_LOADING": "lazy",
101
101
  "IMAGES_DEFAULT_DECODING": "async",
102
+ "HEALTHCHECK_URL": healthcheck_url,
102
103
  })
103
104
  if config:
104
105
  self.config.update(config)
@@ -261,6 +262,17 @@ class Hyperflask(Flask):
261
262
  except TemplateNotFound:
262
263
  return self.make_response(("500 Internal Server Error", 500))
263
264
 
265
+ if self.config['HEALTHCHECK_URL']:
266
+ @self.route(self.config['HEALTHCHECK_URL'])
267
+ def healthcheck():
268
+ if not isinstance(self.db, UndefinedDatabase):
269
+ try:
270
+ with self.db as tx:
271
+ tx.execute("SELECT 1")
272
+ except Exception:
273
+ return {"status": "error"}, 500
274
+ return {"status": "ok"}
275
+
264
276
  def relative_import_name(self, name):
265
277
  return f"{self.import_name}.{name}" if self.import_name != "__main__" else name
266
278
 
@@ -7,6 +7,7 @@ from ..security import generate_csp_policy
7
7
  from ..utils.freezer import StaticMode
8
8
  from .runner import serve_command, run_command, dev_command
9
9
  from .worker import worker_command, scheduler_command
10
+ from .scaffold import gen
10
11
 
11
12
 
12
13
  def _create_app():
@@ -25,11 +26,7 @@ cli.add_command(run_command)
25
26
  cli.add_command(dev_command)
26
27
  cli.add_command(worker_command)
27
28
  cli.add_command(scheduler_command)
28
-
29
-
30
- @cli.command("init")
31
- def init_command():
32
- pass
29
+ cli.add_command(gen)
33
30
 
34
31
 
35
32
  @cli.command("build")
@@ -6,29 +6,36 @@ import multiprocessing
6
6
  import gunicorn.app.base
7
7
  from subprocess import Popen
8
8
  from honcho.manager import Manager as BaseProcessManager
9
+ from honcho.command import _parse_concurrency
10
+ from honcho.environ import parse_procfile
11
+ from honcho.printer import Printer
12
+ from honcho import environ
13
+ from flask_configurator.config import Config
9
14
  from ..factory import load_config
10
15
  from . import reloader # import hacky patch of werkzeug reloader
11
16
 
12
17
 
13
18
  @click.command("serve")
14
- @click.option('--gunicorn-opt', multiple=True)
19
+ @click.option('--gunicorn-config')
20
+ @click.option("--init-db", is_flag=True)
15
21
  @pass_script_info
16
22
  @click.pass_context
17
- def serve_command(ctx, info, host, port, gunicorn_opt, **run_kwargs):
23
+ def serve_command(ctx, info, host, port, gunicorn_config, init_db, **run_kwargs):
24
+ if init_db:
25
+ print("Initializing database...")
26
+ Popen([sys.argv[0], "db", "init"]).wait()
27
+
18
28
  if os.environ.get("FLASK_DEBUG") == "1":
19
29
  ctx.invoke(flask_run_command, host=host, port=port, **run_kwargs)
20
- else:
21
- options = {
22
- 'proc_name': 'hyperflask',
23
- 'bind': f'{host}:{port}',
24
- 'workers': 1 + multiprocessing.cpu_count() * 2,
25
- 'max_requests': 1000,
26
- 'max_requests_jitter': 100
27
- }
28
- for opt in gunicorn_opt:
29
- key, value = opt.split('=', 1)
30
- options[key] = int(value) if value.isdigit() else value
31
- GunicornServer(info.load_app, options).run()
30
+ return
31
+
32
+ GunicornServer(info.load_app, {
33
+ 'proc_name': 'hyperflask',
34
+ 'bind': f'{host}:{port}',
35
+ 'workers': 1 + multiprocessing.cpu_count() * 2,
36
+ 'max_requests': 1000,
37
+ 'max_requests_jitter': 100
38
+ }, gunicorn_config).run()
32
39
 
33
40
 
34
41
  serve_command.params = flask_run_command.params + serve_command.params
@@ -41,13 +48,9 @@ serve_command.params = flask_run_command.params + serve_command.params
41
48
  @click.option('--concurrency')
42
49
  @click.option("--extend-procfile", is_flag=True)
43
50
  @click.option("--init-db/--no-init-db", is_flag=True, default=None)
51
+ @click.option('--gunicorn-config')
44
52
  @click.option("--dev", is_flag=True)
45
- def run_command(processes, host, port, concurrency, extend_procfile, init_db, dev):
46
- from honcho.command import _parse_concurrency
47
- from honcho.environ import parse_procfile
48
- from honcho.printer import Printer
49
- from honcho import environ
50
-
53
+ def run_command(processes, host, port, concurrency, extend_procfile, init_db, gunicorn_config, dev):
51
54
  if dev:
52
55
  os.environ["FLASK_DEBUG"] = "1"
53
56
 
@@ -59,6 +62,10 @@ def run_command(processes, host, port, concurrency, extend_procfile, init_db, de
59
62
  procfile = None
60
63
  config = load_config()
61
64
 
65
+ if not dev and not config.get('SECRET_KEY'):
66
+ print("Critical: No SECRET_KEY set, cannot run in production mode.")
67
+ sys.exit(1)
68
+
62
69
  for filename in [f"Procfile.{config['ENV']}", "Procfile"]:
63
70
  if os.path.exists(filename):
64
71
  with open(filename) as f:
@@ -70,21 +77,13 @@ def run_command(processes, host, port, concurrency, extend_procfile, init_db, de
70
77
  "worker": [sys.argv[0], "worker"],
71
78
  "scheduler": [sys.argv[0], "scheduler"]
72
79
  }
80
+ if gunicorn_config:
81
+ _processes["web"].extend(["--gunicorn-config", gunicorn_config])
73
82
  if dev:
74
83
  _processes["assets"] = [sys.argv[0], "assets", "dev"]
75
84
  _processes["worker"].extend(["-p", "1", "-t", "1"])
76
85
  else:
77
86
  _processes["mercurehub"] = [sys.executable, "-m", "flask_mercure_sse.server", "--port", "$PORT"]
78
- subscriber_secret_key = config.get("MERCURE_SUBSCRIBER_SECRET_KEY", config.get('SECRET_KEY'))
79
- if subscriber_secret_key:
80
- _processes["mercurehub"].extend(["--subscriber-secret", subscriber_secret_key])
81
- else:
82
- print("Warning: No MERCURE_SUBSCRIBER_SECRET_KEY or SECRET_KEY set, Mercure subscriber will not be authenticated.")
83
- publisher_secret_key = config.get("MERCURE_PUBLISHER_SECRET_KEY", config.get('SECRET_KEY'))
84
- if publisher_secret_key:
85
- _processes["mercurehub"].extend(["--publisher-secret", publisher_secret_key])
86
- else:
87
- print("Warning: No MERCURE_PUBLISHER_SECRET_KEY or SECRET_KEY set, cannot publish Mercure messages.")
88
87
 
89
88
  if procfile and extend_procfile:
90
89
  _processes.update(procfile)
@@ -93,13 +92,19 @@ def run_command(processes, host, port, concurrency, extend_procfile, init_db, de
93
92
 
94
93
  processes = {name: " ".join(_processes[name]) if isinstance(_processes[name], list) else _processes[name]
95
94
  for name in _processes if not processes or name in processes}
95
+ env = {}
96
+ if not dev:
97
+ env = {
98
+ "MERCURE_SUBSCRIBER_SECRET_KEY": config.get("MERCURE_SUBSCRIBER_SECRET_KEY", config['SECRET_KEY']),
99
+ "MERCURE_PUBLISHER_SECRET_KEY": config.get("MERCURE_PUBLISHER_SECRET_KEY", config['SECRET_KEY']),
100
+ }
96
101
 
97
102
  manager = ProcessManager(Printer(sys.stdout, colour=True))
98
-
99
103
  for p in environ.expand_processes(processes,
100
104
  concurrency=_parse_concurrency(concurrency),
101
105
  port=port):
102
106
  e = os.environ.copy()
107
+ e.update(env)
103
108
  e.update(p.env)
104
109
  manager.add_process(p.name, p.cmd, quiet=p.quiet, env=e)
105
110
 
@@ -130,14 +135,19 @@ class ProcessManager(BaseProcessManager):
130
135
 
131
136
 
132
137
  class GunicornServer(gunicorn.app.base.BaseApplication):
133
- def __init__(self, app_loader, options=None):
138
+ def __init__(self, app_loader, config_defaults=None, config_filename=None):
134
139
  self.app_loader = app_loader
135
- self.options = options or {}
140
+ self.config_defaults = config_defaults or {}
141
+ self.config_filename = config_filename
136
142
  super().__init__()
137
143
 
138
144
  def load_config(self):
139
- config = {key: value for key, value in self.options.items()
140
- if key in self.cfg.settings and value is not None}
145
+ config = Config("", self.config_defaults, env_prefix="GUNICORN")
146
+ config.load(self.config_filename, default_env="production")
147
+
148
+ config = {key.lower(): value for key, value in config.items()
149
+ if key.lower() in self.cfg.settings and value is not None}
150
+
141
151
  for key, value in config.items():
142
152
  self.cfg.set(key.lower(), value)
143
153
 
@@ -0,0 +1,32 @@
1
+ from flask.cli import AppGroup, with_appcontext
2
+ import random
3
+ import click
4
+ import os
5
+
6
+
7
+ gen = AppGroup('gen', help='Generate various files and code snippets.')
8
+
9
+
10
+ @gen.command('secret-key')
11
+ @click.option('--save/--no-save', is_flag=True, default=True, help='Save the generated key to the .env file.')
12
+ def generate_secret_key_command(save):
13
+ key = generate_secret_key()
14
+ if save:
15
+ save_env_var("FLASK_SECRET_KEY", key)
16
+ click.echo(key)
17
+
18
+
19
+ def generate_secret_key():
20
+ return ''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)])
21
+
22
+
23
+ def save_env_var(key, value):
24
+ env = []
25
+ if os.path.exists(".env"):
26
+ with open(".env", "r") as f:
27
+ env = f.readlines()
28
+ with open(".env", "w") as f:
29
+ for line in env:
30
+ if not line.startswith(f"{key}="):
31
+ f.write(f"{line}\n")
32
+ f.write(f"{key}={value}\n")
@@ -7,7 +7,6 @@ from jinjapy import extract_frontmatter
7
7
  from ..utils.freezer import dynamic
8
8
  import importlib
9
9
  import re
10
- import os
11
10
 
12
11
 
13
12
  class BaseComponentAdapter:
@@ -71,8 +70,9 @@ class ComponentAdapter(BaseComponentAdapter):
71
70
  source, frontmatter = extract_frontmatter(f.read())
72
71
  if not self.template:
73
72
  frontmatter = source
73
+ if not frontmatter:
74
+ return []
74
75
  return [m.upper() for m in http_method_funcs if re.search(rf"^(def\s+{m}\()|{m}\s*=", frontmatter, re.MULTILINE)]
75
- return []
76
76
 
77
77
  def render(self, *args, **kwargs):
78
78
  caller = kwargs.pop('caller', None)
File without changes