Skip to content

Commit 9438bf6

Browse files
feat: recommend granian for wsgi (#15725)
* feat: recommend granian for wsgi This is what our Docker container will use, see WeblateOrg/docker#3510 * chore(deps): update lockfile --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent ef7113b commit 9438bf6

File tree

10 files changed

+311
-20
lines changed

10 files changed

+311
-20
lines changed

docs/admin/install.rst

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ Django REST Framework
272272
- Hosted Weblate integration
273273

274274
* - ``wsgi``
275-
- | `gunicorn <https://pypi.org/project/gunicorn>`_
275+
- | `granian <https://pypi.org/project/granian>`_
276276
- wsgi server for Weblate
277277

278278
* - ``zxcvbn``
@@ -768,8 +768,9 @@ Client protocol
768768
* :ref:`spam-protection`
769769
* :ref:`rate-limit`
770770
* :ref:`audit-log`
771-
* :ref:`uwsgi`
771+
* :ref:`nginx-granian`
772772
* :ref:`nginx-gunicorn`
773+
* :ref:`uwsgi`
773774
* :ref:`apache`
774775
* :ref:`apache-gunicorn`
775776
* :setting:`IP_BEHIND_REVERSE_PROXY`
@@ -1409,8 +1410,14 @@ For testing purposes, you can use the built-in web server in Django:
14091410

14101411
The Django built-in server serves static files only with :setting:`DEBUG`
14111412
enabled as it is intended for development only. For production use, please
1412-
see WSGI setups in :ref:`uwsgi`, :ref:`apache`, :ref:`apache-gunicorn`, and
1413-
:ref:`static-files`.
1413+
see WSGI setups:
1414+
1415+
* :ref:`nginx-granian`
1416+
* :ref:`nginx-gunicorn`
1417+
* :ref:`uwsgi`
1418+
* :ref:`apache`
1419+
* :ref:`apache-gunicorn`
1420+
* :ref:`static-files`
14141421

14151422
.. _static-files:
14161423

@@ -1435,6 +1442,8 @@ use that for the following paths:
14351442

14361443
.. seealso::
14371444

1445+
* :ref:`nginx-granian`
1446+
* :ref:`nginx-gunicorn`
14381447
* :ref:`uwsgi`
14391448
* :ref:`apache`
14401449
* :ref:`apache-gunicorn`
@@ -1461,6 +1470,23 @@ configuration, but this might need customization for your environment.
14611470
* :setting:`CSP_FONT_SRC`
14621471
* :setting:`CSP_FORM_SRC`
14631472

1473+
.. _nginx-granian:
1474+
1475+
Sample configuration for NGINX and Granian
1476+
+++++++++++++++++++++++++++++++++++++++++++
1477+
1478+
The following configuration runs Weblate using Granian the NGINX webserver:
1479+
1480+
.. literalinclude:: ../../weblate/examples/weblate.nginx.granian.conf
1481+
:language: nginx
1482+
:caption: weblate/examples/weblate.nginx.granian.conf
1483+
1484+
.. seealso::
1485+
1486+
* :ref:`running-granian`
1487+
* https://github.com/emmett-framework/granian
1488+
* :doc:`django:howto/deployment/wsgi/index`
1489+
14641490
.. _nginx-gunicorn:
14651491

14661492
Sample configuration for NGINX and Gunicorn
@@ -1549,18 +1575,40 @@ The following configuration runs Weblate in Gunicorn and Apache 2.4
15491575
* :doc:`django:howto/deployment/wsgi/gunicorn`
15501576

15511577

1552-
.. _running-gunicorn:
1578+
.. _running-granian:
15531579

1554-
Sample configuration to start Gunicorn
1555-
++++++++++++++++++++++++++++++++++++++
1580+
Sample configuration to start Granian
1581+
+++++++++++++++++++++++++++++++++++++
15561582

15571583
Weblate has `wsgi` optional dependency (see :ref:`python-deps`) that will
1558-
install everything you need to run Gunicorn. When installing Weblate you can specify it as:
1584+
install everything you need to run Granian. When installing Weblate you can specify it as:
15591585

15601586
.. code-block:: shell
15611587
15621588
uv pip install Weblate[all,wsgi]
15631589
1590+
Once you have Granian installed, you can run it. This is usually done at the
1591+
system level. The following examples show starting via systemd:
1592+
1593+
.. literalinclude:: ../../weblate/examples/granian.service
1594+
:caption: /etc/systemd/system/granian.service
1595+
:language: ini
1596+
1597+
.. seealso::
1598+
1599+
* https://github.com/emmett-framework/granian
1600+
* :doc:`django:howto/deployment/wsgi/index`
1601+
1602+
.. _running-gunicorn:
1603+
1604+
Sample configuration to start Gunicorn
1605+
++++++++++++++++++++++++++++++++++++++
1606+
1607+
Gunicorn has to be installed separately:
1608+
1609+
.. code-block:: shell
1610+
1611+
uv pip install gunicorn
15641612
15651613
Once you have Gunicorn installed, you can run it. This is usually done at the
15661614
system level. The following examples show starting via systemd:

docs/admin/management.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,10 @@ Imports given content into Weblate, useful for benchmarking.
176176
:caption: Example of memory profiling
177177
178178
# Run benchmark under memray
179-
memray run ./manage.py benchmark --project benchmark --filemask '*.tbx' --format tbx --zipfile /tmp/MicrosoftTermCollection2.zip
179+
uvx memray run ./manage.py benchmark --project benchmark --filemask '*.tbx' --format tbx --zipfile /tmp/MicrosoftTermCollection2.zip
180180
181181
# Render the profile
182-
memray flamegraph ./memray-manage.py.2554179.bin
182+
uvx memray flamegraph ./memray-manage.py.2554179.bin
183183
184184
# Display it
185185
fixefox memray-flamegraph-manage.py.2554179.html

docs/changes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Please follow :ref:`generic-upgrade-instructions` in order to perform update.
4040

4141
* The distributed locking now uses a different implementation and that introduced several changes in :file:`settings_example.py`.
4242
* There are several changes in :file:`settings_example.py`, most notable are changed settings ``CRISPY_ALLOWED_TEMPLATE_PACKS`` and ``INSTALLED_APPS``; please adjust your settings accordingly.
43+
* Docker container is now using :program:`granian`.
4344

4445
.. rubric:: Contributors
4546

docs/specs/sbom/partial/python.json

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2887,6 +2887,50 @@
28872887
"type": "library",
28882888
"version": "1.70.0"
28892889
},
2890+
{
2891+
"bom-ref": "granian==2.5.0",
2892+
"description": "A Rust HTTP server for Python applications",
2893+
"externalReferences": [
2894+
{
2895+
"comment": "from packaging metadata Project-URL: Source",
2896+
"type": "other",
2897+
"url": "https://github.com/emmett-framework/granian"
2898+
},
2899+
{
2900+
"comment": "from packaging metadata Project-URL: Funding",
2901+
"type": "other",
2902+
"url": "https://github.com/sponsors/gi0baro"
2903+
},
2904+
{
2905+
"comment": "from packaging metadata Project-URL: Homepage",
2906+
"type": "website",
2907+
"url": "https://github.com/emmett-framework/granian"
2908+
},
2909+
{
2910+
"comment": "from packaging metadata: Home-page",
2911+
"type": "website",
2912+
"url": "https://github.com/emmett-framework/granian"
2913+
}
2914+
],
2915+
"licenses": [
2916+
{
2917+
"license": {
2918+
"acknowledgement": "declared",
2919+
"id": "BSD-3-Clause"
2920+
}
2921+
},
2922+
{
2923+
"license": {
2924+
"acknowledgement": "declared",
2925+
"name": "License :: OSI Approved :: BSD License"
2926+
}
2927+
}
2928+
],
2929+
"name": "granian",
2930+
"purl": "pkg:pypi/granian@2.5.0",
2931+
"type": "library",
2932+
"version": "2.5.0"
2933+
},
28902934
{
28912935
"bom-ref": "grpc-google-iam-v1==0.14.2",
28922936
"description": "IAM API client library",
@@ -7299,6 +7343,12 @@
72997343
],
73007344
"ref": "googleapis-common-protos==1.70.0"
73017345
},
7346+
{
7347+
"dependsOn": [
7348+
"click==8.2.1"
7349+
],
7350+
"ref": "granian==2.5.0"
7351+
},
73027352
{
73037353
"dependsOn": [
73047354
"googleapis-common-protos==1.70.0",
@@ -7932,6 +7982,7 @@
79327982
"git-review==2.5.0",
79337983
"google-cloud-storage==3.2.0",
79347984
"google-cloud-translate==3.21.1",
7985+
"granian==2.5.0",
79357986
"gunicorn==23.0.0",
79367987
"hiredis==3.2.1",
79377988
"html2text==2025.4.15",
@@ -8158,5 +8209,5 @@
81588209
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
81598210
"bomFormat": "CycloneDX",
81608211
"specVersion": "1.6",
8161-
"serialNumber": "urn:uuid:4633fc24-47b6-511a-bc7c-7a2a69f75f3c"
8212+
"serialNumber": "urn:uuid:6d5ce757-c621-5a78-bda5-02a8fd073444"
81628213
}

docs/specs/sbom/sbom.json

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"bomFormat": "CycloneDX",
33
"specVersion": "1.6",
4-
"serialNumber": "urn:uuid:c34a953d-25cf-5b92-b89f-46436e94a1eb",
4+
"serialNumber": "urn:uuid:2ff7c1a6-e908-55d4-b8cf-03ab981dd7bc",
55
"version": 1,
66
"metadata": {
77
"tools": [
@@ -3968,6 +3968,50 @@
39683968
}
39693969
]
39703970
},
3971+
{
3972+
"type": "library",
3973+
"bom-ref": "granian==2.5.0",
3974+
"name": "granian",
3975+
"version": "2.5.0",
3976+
"description": "A Rust HTTP server for Python applications",
3977+
"licenses": [
3978+
{
3979+
"license": {
3980+
"id": "BSD-3-Clause",
3981+
"acknowledgement": "declared"
3982+
}
3983+
},
3984+
{
3985+
"license": {
3986+
"name": "License :: OSI Approved :: BSD License",
3987+
"acknowledgement": "declared"
3988+
}
3989+
}
3990+
],
3991+
"purl": "pkg:pypi/granian@2.5.0",
3992+
"externalReferences": [
3993+
{
3994+
"url": "https://github.com/emmett-framework/granian",
3995+
"type": "other",
3996+
"comment": "from packaging metadata Project-URL: Source"
3997+
},
3998+
{
3999+
"url": "https://github.com/sponsors/gi0baro",
4000+
"type": "other",
4001+
"comment": "from packaging metadata Project-URL: Funding"
4002+
},
4003+
{
4004+
"url": "https://github.com/emmett-framework/granian",
4005+
"type": "website",
4006+
"comment": "from packaging metadata Project-URL: Homepage"
4007+
},
4008+
{
4009+
"url": "https://github.com/emmett-framework/granian",
4010+
"type": "website",
4011+
"comment": "from packaging metadata: Home-page"
4012+
}
4013+
]
4014+
},
39714015
{
39724016
"type": "library",
39734017
"bom-ref": "grpc-google-iam-v1==0.14.2",
@@ -8512,6 +8556,12 @@
85128556
"protobuf==6.31.1"
85138557
]
85148558
},
8559+
{
8560+
"ref": "granian==2.5.0",
8561+
"dependsOn": [
8562+
"click==8.2.1"
8563+
]
8564+
},
85158565
{
85168566
"ref": "grpc-google-iam-v1==0.14.2",
85178567
"dependsOn": [
@@ -9146,6 +9196,7 @@
91469196
"git-review==2.5.0",
91479197
"google-cloud-storage==3.2.0",
91489198
"google-cloud-translate==3.21.1",
9199+
"granian==2.5.0",
91499200
"gunicorn==23.0.0",
91509201
"hiredis==3.2.1",
91519202
"html2text==2025.4.15",

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ wllegal = [
235235
"wllegal>=2024.6"
236236
]
237237
wsgi = [
238+
"granian==2.5.0",
239+
# TODO: Remove gunicorn once dev-docker is based on the Weblate 5.13 container
238240
"gunicorn==23.0.0"
239241
]
240242
zxcvbn = [

0 commit comments

Comments
 (0)