commit d95f211d84f1c02c22c1d054e426959d3e49513d
parent cbe1a1290c399b397a795d49715b9fa03dd5bf52
Author: Florian Dold <florian@dold.me>
Date: Wed, 16 Oct 2024 14:52:53 +0200
use gunicorn instead of uwsgi to serve the apps
The UWSGI project is in maintainence mode and overkill for what we need.
Diffstat:
9 files changed, 220 insertions(+), 197 deletions(-)
diff --git a/Makefile b/Makefile
@@ -19,8 +19,6 @@ setup-arch:
echo "Installing Pip Dependencies..."
echo "-> Installing wheel..."
pip install wheel
- echo "-> Installing uwsgi..."
- pip install uwsgi
echo "-> Installing lxml..."
pip install lxml
echo "-> Installing poetry..."
@@ -40,8 +38,6 @@ setup-deb:
echo "Installing Pip Dependencies..."
echo "-> Installing wheel..."
pip install wheel
- echo "-> Installing uwsgi..."
- pip install uwsgi
echo "-> Installing lxml..."
pip install lxml
echo "-> Installing poetry..."
diff --git a/debian/control b/debian/control
@@ -18,7 +18,6 @@ Architecture: all
Depends: ${misc:Depends},
python3-flask,
python3-flask-babel,
- uwsgi,
python3-requests,
python3-bs4
Recommends: apache2 | nginx | httpd
diff --git a/poetry.lock b/poetry.lock
@@ -1,10 +1,15 @@
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
+
[[package]]
name = "babel"
version = "2.9.1"
description = "Internationalization utilities"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"},
+ {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"},
+]
[package.dependencies]
pytz = ">=2015.7"
@@ -13,9 +18,12 @@ pytz = ">=2015.7"
name = "beautifulsoup4"
version = "4.10.0"
description = "Screen-scraping library"
-category = "main"
optional = false
python-versions = ">3.0.0"
+files = [
+ {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"},
+ {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"},
+]
[package.dependencies]
soupsieve = ">1.2"
@@ -28,28 +36,37 @@ lxml = ["lxml"]
name = "certifi"
version = "2021.10.8"
description = "Python package for providing Mozilla's CA Bundle."
-category = "main"
optional = false
python-versions = "*"
+files = [
+ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
+ {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
+]
[[package]]
name = "charset-normalizer"
version = "2.0.7"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-category = "main"
optional = false
python-versions = ">=3.5.0"
+files = [
+ {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
+ {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"},
+]
[package.extras]
-unicode_backport = ["unicodedata2"]
+unicode-backport = ["unicodedata2"]
[[package]]
name = "click"
version = "8.0.3"
description = "Composable command line interface toolkit"
-category = "main"
optional = false
python-versions = ">=3.6"
+files = [
+ {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
+ {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
+]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
@@ -58,17 +75,23 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
+ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+]
[[package]]
name = "flask"
version = "2.0.2"
description = "A simple framework for building complex web applications."
-category = "main"
optional = false
python-versions = ">=3.6"
+files = [
+ {file = "Flask-2.0.2-py3-none-any.whl", hash = "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"},
+ {file = "Flask-2.0.2.tar.gz", hash = "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2"},
+]
[package.dependencies]
click = ">=7.1.2"
@@ -84,9 +107,12 @@ dotenv = ["python-dotenv"]
name = "flask-babel"
version = "2.0.0"
description = "Adds i18n/l10n support to Flask applications"
-category = "main"
optional = false
python-versions = "*"
+files = [
+ {file = "Flask-Babel-2.0.0.tar.gz", hash = "sha256:f9faf45cdb2e1a32ea2ec14403587d4295108f35017a7821a2b1acb8cfd9257d"},
+ {file = "Flask_Babel-2.0.0-py3-none-any.whl", hash = "sha256:e6820a052a8d344e178cdd36dd4bb8aea09b4bda3d5f9fa9f008df2c7f2f5468"},
+]
[package.dependencies]
Babel = ">=2.3"
@@ -95,31 +121,61 @@ Jinja2 = ">=2.5"
pytz = "*"
[package.extras]
-dev = ["pytest", "pytest-mock", "bumpversion", "ghp-import", "sphinx", "pallets-sphinx-themes"]
+dev = ["Pallets-Sphinx-Themes", "bumpversion", "ghp-import", "pytest", "pytest-mock", "sphinx"]
+
+[[package]]
+name = "gunicorn"
+version = "23.0.0"
+description = "WSGI HTTP Server for UNIX"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"},
+ {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"},
+]
+
+[package.dependencies]
+packaging = "*"
+
+[package.extras]
+eventlet = ["eventlet (>=0.24.1,!=0.36.0)"]
+gevent = ["gevent (>=1.4.0)"]
+setproctitle = ["setproctitle"]
+testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
+tornado = ["tornado (>=0.2)"]
[[package]]
name = "idna"
version = "3.3"
description = "Internationalized Domain Names in Applications (IDNA)"
-category = "main"
optional = false
python-versions = ">=3.5"
+files = [
+ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
+ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
+]
[[package]]
name = "itsdangerous"
version = "2.0.1"
description = "Safely pass data to untrusted environments and back."
-category = "main"
optional = false
python-versions = ">=3.6"
+files = [
+ {file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"},
+ {file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"},
+]
[[package]]
name = "jinja2"
version = "3.0.2"
description = "A very fast and expressive template engine."
-category = "main"
optional = false
python-versions = ">=3.6"
+files = [
+ {file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"},
+ {file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"},
+]
[package.dependencies]
MarkupSafe = ">=2.0"
@@ -131,119 +187,9 @@ i18n = ["Babel (>=2.7)"]
name = "markupsafe"
version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "pytz"
-version = "2021.3"
-description = "World timezone definitions, modern and historical"
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "requests"
-version = "2.26.0"
-description = "Python HTTP for Humans."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
-idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
-urllib3 = ">=1.21.1,<1.27"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
-use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
-
-[[package]]
-name = "soupsieve"
-version = "2.2.1"
-description = "A modern CSS selector implementation for Beautiful Soup."
-category = "main"
optional = false
python-versions = ">=3.6"
-
-[[package]]
-name = "urllib3"
-version = "1.26.7"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-
-[package.extras]
-brotli = ["brotlipy (>=0.6.0)"]
-secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
-
-[[package]]
-name = "werkzeug"
-version = "2.0.2"
-description = "The comprehensive WSGI web application library."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-watchdog = ["watchdog"]
-
-[metadata]
-lock-version = "1.1"
-python-versions = "^3.8"
-content-hash = "0a8bd3c73fbceb159d7af4b517aedb0d80060648bc5779bc0e05181d4ed4da20"
-
-[metadata.files]
-babel = [
- {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"},
- {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"},
-]
-beautifulsoup4 = [
- {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"},
- {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"},
-]
-certifi = [
- {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
- {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
-]
-charset-normalizer = [
- {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
- {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"},
-]
-click = [
- {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
- {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
-]
-colorama = [
- {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
- {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
-]
-flask = [
- {file = "Flask-2.0.2-py3-none-any.whl", hash = "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"},
- {file = "Flask-2.0.2.tar.gz", hash = "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2"},
-]
-flask-babel = [
- {file = "Flask-Babel-2.0.0.tar.gz", hash = "sha256:f9faf45cdb2e1a32ea2ec14403587d4295108f35017a7821a2b1acb8cfd9257d"},
- {file = "Flask_Babel-2.0.0-py3-none-any.whl", hash = "sha256:e6820a052a8d344e178cdd36dd4bb8aea09b4bda3d5f9fa9f008df2c7f2f5468"},
-]
-idna = [
- {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
- {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
-]
-itsdangerous = [
- {file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"},
- {file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"},
-]
-jinja2 = [
- {file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"},
- {file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"},
-]
-markupsafe = [
+files = [
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
@@ -314,23 +260,92 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
-pytz = [
+
+[[package]]
+name = "packaging"
+version = "24.1"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
+]
+
+[[package]]
+name = "pytz"
+version = "2021.3"
+description = "World timezone definitions, modern and historical"
+optional = false
+python-versions = "*"
+files = [
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
]
-requests = [
+
+[[package]]
+name = "requests"
+version = "2.26.0"
+description = "Python HTTP for Humans."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+files = [
{file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
{file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
]
-soupsieve = [
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
+idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"]
+
+[[package]]
+name = "soupsieve"
+version = "2.2.1"
+description = "A modern CSS selector implementation for Beautiful Soup."
+optional = false
+python-versions = ">=3.6"
+files = [
{file = "soupsieve-2.2.1-py3-none-any.whl", hash = "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"},
{file = "soupsieve-2.2.1.tar.gz", hash = "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc"},
]
-urllib3 = [
+
+[[package]]
+name = "urllib3"
+version = "1.26.7"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+files = [
{file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"},
{file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"},
]
-werkzeug = [
+
+[package.extras]
+brotli = ["brotlipy (>=0.6.0)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
+[[package]]
+name = "werkzeug"
+version = "2.0.2"
+description = "The comprehensive WSGI web application library."
+optional = false
+python-versions = ">=3.6"
+files = [
{file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
{file = "Werkzeug-2.0.2.tar.gz", hash = "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"},
]
+
+[package.extras]
+watchdog = ["watchdog"]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.8"
+content-hash = "3fb5b3be67c35ccc731b74abc54122853c7074d27c4dad6d2eda0d6cbcaf0864"
diff --git a/pyproject.toml b/pyproject.toml
@@ -36,6 +36,7 @@ Flask = "^2.0.2"
requests = "^2.26.0"
beautifulsoup4 = "^4.10.0"
Flask-Babel = "^2.0.0"
+gunicorn = "^23.0.0"
[tool.poetry.dev-dependencies]
diff --git a/talermerchantdemos/appconfig.py b/talermerchantdemos/appconfig.py
@@ -0,0 +1,27 @@
+##
+# This file is part of GNU TALER.
+# Copyright (C) 2024 Taler Systems SA
+#
+# TALER is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free Software
+# Foundation; either version 2.1, or (at your option) any later version.
+#
+# TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along with
+# GNU TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+# @author Florian Dold
+
+import os
+from .util.talerconfig import TalerConfig
+
+def load_taler_config():
+ # The (WSGI) host passes the configuration endpoint via
+ # an environment variable.
+ config_filename = os.environ["TALER_CONFIG_FILENAME"]
+ if config_filename == "":
+ config_filename = None
+ return TalerConfig.from_file(config_filename)
diff --git a/talermerchantdemos/blog/blog.py b/talermerchantdemos/blog/blog.py
@@ -24,7 +24,6 @@ import traceback
import uuid
import base64
import flask
-import uwsgi
from flask import request, url_for
from flask_babel import Babel
from flask_babel import refresh
@@ -34,6 +33,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
import time
import sys
from urllib.parse import urljoin, urlencode, urlparse
+from ..appconfig import load_taler_config
from ..util.talerconfig import TalerConfig, ConfigurationError
from ..blog.content import ARTICLES, get_article_contents
from talermerchantdemos.httpcommon import (
@@ -65,11 +65,7 @@ app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8")
logging.basicConfig()
LOGGER = logging.getLogger(__name__)
-config_filename = uwsgi.opt["config_filename"].decode("utf-8")
-if config_filename == "":
- config_filename = None
-
-config = TalerConfig.from_file(config_filename)
+config = load_taler_config()
CURRENCY = config["taler"]["currency"].value_string(required=True)
diff --git a/talermerchantdemos/cli.py b/talermerchantdemos/cli.py
@@ -27,42 +27,16 @@ import os
import site
from .util.talerconfig import TalerConfig, ConfigurationError
-LOGGER = logging.getLogger(__name__)
-# No perfect match to our logging format, but good enough ...
-UWSGI_LOGFMT = "%(ltime) %(proto) %(method) %(uri) %(proto) => %(status)"
-
-arg_venvpath = ["-H", sys.prefix]
+import importlib
+import multiprocessing
+import gunicorn.app.base
-# Argument to tell uWSGI to load the python plugin.
-# This hack is required, because on systems where the plugin is statically linked,
-# loading it causes an error.
-arg_load_python = "--if-not-plugin python --plugins python --endif".split(" ")
+LOGGER = logging.getLogger(__name__)
-##
-# This function interprets the 'serve-http' subcommand.
-# The effect it to launch the blog HTTP service.
-#
-# @param args command line options.
-def handle_serve_http(config_filename, which_shop, port=None):
+def bindspec(config_filename, which_shop, port):
config = TalerConfig.from_file(config_filename)
confsection = f"frontend-demo-{which_shop}"
- currency = config["taler"]["currency"].value_string(required=True)
- cfg_arg = "" if config_filename is None else config_filename
- params = [
- "uwsgi",
- "uwsgi",
- *arg_venvpath,
- *arg_load_python,
- "--master",
- "--die-on-term",
- "--log-format",
- UWSGI_LOGFMT,
- "--set-ph",
- f"config_filename={cfg_arg}",
- "--module",
- f"talermerchantdemos.{which_shop}:app",
- ]
# Takes precedence.
if port:
@@ -83,22 +57,36 @@ def handle_serve_http(config_filename, which_shop, port=None):
if not port_launch:
sys.stderr.write("Port number wasn't found in config and in arguments.")
exit(1)
- params.extend(["--http", f":{port_launch}"])
+ return dict(bind=f"0.0.0.0:{port_launch}")
if http_serve == "unix":
path = config[confsection]["http_unixpath"].value_filename(required=True)
mode = config[confsection]["http_unixpath_mode"].value_filename(required=True)
- params.extend(["--http-socket", path])
- params.extend(["--chmod-socket=" + mode])
- os.makedirs(os.path.dirname(path), exist_ok=True)
-
- try:
- os.execlp(*params)
- except:
- sys.stderr.write(
- "Failed to start uwsgi. Please make sure to install uwsgi for Python3."
- )
- sys.exit(1)
+ return dict(bind=f"unix:{path}", umask=mode)
+
+
+def number_of_workers():
+ return (multiprocessing.cpu_count() * 2) + 1
+
+
+class StandaloneApplication(gunicorn.app.base.BaseApplication):
+
+ def __init__(self, app, options=None):
+ self.options = options or {}
+ self.application = app
+ super().__init__()
+
+ def load_config(self):
+ config = {
+ key: value
+ for key, value in self.options.items()
+ if key in self.cfg.settings and value is not None
+ }
+ for key, value in config.items():
+ self.cfg.set(key.lower(), value)
+
+ def load(self):
+ return self.application
@click.command("Global shop launcher")
@@ -116,7 +104,16 @@ def demos(config, http_port, which_shop):
if which_shop not in ["blog", "donations", "landing"]:
print("Please use a valid shop name: blog, donations, landing.")
sys.exit(1)
- handle_serve_http(config, which_shop, http_port)
+
+ options = {
+ "workers": number_of_workers(),
+ **bindspec(config, which_shop, http_port),
+ }
+ mod = f"talermerchantdemos.{which_shop}"
+ os.environ["TALER_CONFIG_FILENAME"] = config or ""
+ app = importlib.import_module(mod).app
+ StandaloneApplication(app, options).run()
-demos()
+if __name__ == "__main__":
+ demos()
diff --git a/talermerchantdemos/donations/donations.py b/talermerchantdemos/donations/donations.py
@@ -20,7 +20,6 @@
import base64
import logging
import flask
-import uwsgi
from flask import request, url_for
from flask_babel import Babel
from flask_babel import refresh
@@ -31,9 +30,9 @@ import os
import time
import traceback
import urllib
-from ..util.talerconfig import TalerConfig, ConfigurationError
from urllib.parse import urljoin
from ..httpcommon import backend_post, backend_get, make_utility_processor, get_locale
+from ..appconfig import load_taler_config
import sys
if not sys.version_info.major == 3 and sys.version_info.minor >= 6:
@@ -60,10 +59,7 @@ BABEL_TRANSLATION_DIRECTORIES = "../translations"
babel = Babel(app)
babel.localeselector(get_locale)
-config_filename = uwsgi.opt["config_filename"].decode("utf-8")
-if config_filename == "":
- config_filename = None
-config = TalerConfig.from_file(config_filename)
+config = load_taler_config()
CURRENCY = config["taler"]["currency"].value_string(required=True)
diff --git a/talermerchantdemos/landing/landing.py b/talermerchantdemos/landing/landing.py
@@ -22,7 +22,6 @@ import datetime
import base64
import logging
import flask
-import uwsgi
import werkzeug
from flask import request, url_for
from flask_babel import Babel
@@ -31,6 +30,7 @@ from flask_babel import force_locale
from flask_babel import gettext
from werkzeug.middleware.proxy_fix import ProxyFix
import traceback
+from ..appconfig import load_taler_config
from ..util.talerconfig import TalerConfig, ConfigurationError
from ..httpcommon import (
backend_get,
@@ -58,11 +58,7 @@ app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8")
logging.basicConfig()
LOGGER = logging.getLogger(__name__)
-config_filename = uwsgi.opt["config_filename"].decode("utf-8")
-if config_filename == "":
- config_filename = None
-
-config = TalerConfig.from_file(config_filename)
+config = load_taler_config()
CURRENCY = config["taler"]["currency"].value_string(required=True)