cherrypy-foundation 1.0.0__py3-none-any.whl → 1.0.0a1__py3-none-any.whl

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 (68) hide show
  1. cherrypy_foundation/components/ColorModes.jinja +4 -5
  2. cherrypy_foundation/components/Datatable.jinja +2 -2
  3. cherrypy_foundation/components/Datatable.js +2 -2
  4. cherrypy_foundation/components/Field.jinja +6 -16
  5. cherrypy_foundation/components/Fields.jinja +2 -0
  6. cherrypy_foundation/components/Typeahead.css +1 -6
  7. cherrypy_foundation/components/Typeahead.jinja +2 -2
  8. cherrypy_foundation/components/__init__.py +2 -2
  9. cherrypy_foundation/components/tests/test_static.py +1 -1
  10. cherrypy_foundation/error_page.py +17 -20
  11. cherrypy_foundation/flash.py +15 -17
  12. cherrypy_foundation/form.py +2 -2
  13. cherrypy_foundation/logging.py +2 -2
  14. cherrypy_foundation/passwd.py +2 -2
  15. cherrypy_foundation/plugins/db.py +9 -35
  16. cherrypy_foundation/plugins/ldap.py +38 -46
  17. cherrypy_foundation/plugins/restapi.py +1 -1
  18. cherrypy_foundation/plugins/scheduler.py +84 -208
  19. cherrypy_foundation/plugins/smtp.py +46 -78
  20. cherrypy_foundation/plugins/tests/test_db.py +4 -4
  21. cherrypy_foundation/plugins/tests/test_ldap.py +3 -76
  22. cherrypy_foundation/plugins/tests/test_scheduler.py +50 -58
  23. cherrypy_foundation/plugins/tests/test_smtp.py +7 -40
  24. cherrypy_foundation/tests/__init__.py +0 -72
  25. cherrypy_foundation/tests/test_error_page.py +1 -7
  26. cherrypy_foundation/tests/test_passwd.py +2 -2
  27. cherrypy_foundation/tools/auth.py +38 -59
  28. cherrypy_foundation/tools/auth_mfa.py +88 -89
  29. cherrypy_foundation/tools/errors.py +27 -0
  30. cherrypy_foundation/tools/i18n.py +153 -246
  31. cherrypy_foundation/tools/jinja2.py +13 -29
  32. cherrypy_foundation/tools/ratelimit.py +27 -37
  33. cherrypy_foundation/tools/secure_headers.py +5 -1
  34. cherrypy_foundation/tools/sessions_timeout.py +21 -23
  35. cherrypy_foundation/tools/tests/locales/en/LC_MESSAGES/messages.mo +0 -0
  36. cherrypy_foundation/tools/tests/locales/{de → en}/LC_MESSAGES/messages.po +2 -2
  37. cherrypy_foundation/tools/tests/test_auth.py +4 -21
  38. cherrypy_foundation/tools/tests/test_i18n.py +6 -81
  39. cherrypy_foundation/tools/tests/test_ratelimit.py +2 -2
  40. cherrypy_foundation/url.py +25 -25
  41. cherrypy_foundation/widgets.py +2 -2
  42. cherrypy_foundation-1.0.0a1.dist-info/METADATA +42 -0
  43. {cherrypy_foundation-1.0.0.dist-info → cherrypy_foundation-1.0.0a1.dist-info}/RECORD +46 -65
  44. {cherrypy_foundation-1.0.0.dist-info → cherrypy_foundation-1.0.0a1.dist-info}/WHEEL +1 -1
  45. cherrypy_foundation/components/Flash.jinja +0 -13
  46. cherrypy_foundation/components/LocaleSelection.jinja +0 -13
  47. cherrypy_foundation/components/LocaleSelection.js +0 -26
  48. cherrypy_foundation/plugins/tests/test_scheduler_db.py +0 -107
  49. cherrypy_foundation/sessions.py +0 -93
  50. cherrypy_foundation/tests/templates/test_flash.html +0 -9
  51. cherrypy_foundation/tests/templates/test_form.html +0 -16
  52. cherrypy_foundation/tests/templates/test_url.html +0 -15
  53. cherrypy_foundation/tests/test_flash.py +0 -61
  54. cherrypy_foundation/tests/test_form.py +0 -148
  55. cherrypy_foundation/tests/test_logging.py +0 -78
  56. cherrypy_foundation/tests/test_sessions.py +0 -89
  57. cherrypy_foundation/tests/test_url.py +0 -161
  58. cherrypy_foundation/tools/tests/components/Button.jinja +0 -2
  59. cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.mo +0 -0
  60. cherrypy_foundation/tools/tests/templates/test_jinja2.html +0 -11
  61. cherrypy_foundation/tools/tests/templates/test_jinjax.html +0 -9
  62. cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html +0 -22
  63. cherrypy_foundation/tools/tests/test_auth_mfa.py +0 -369
  64. cherrypy_foundation/tools/tests/test_jinja2.py +0 -153
  65. cherrypy_foundation/tools/tests/test_secure_headers.py +0 -200
  66. cherrypy_foundation-1.0.0.dist-info/METADATA +0 -71
  67. {cherrypy_foundation-1.0.0.dist-info → cherrypy_foundation-1.0.0a1.dist-info}/licenses/LICENSE.md +0 -0
  68. {cherrypy_foundation-1.0.0.dist-info → cherrypy_foundation-1.0.0a1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
- # Cherrypy-foundation
2
- # Copyright (C) 2026 IKUS Software
1
+ # CherryPy Foundation
2
+ # Copyright (C) 2025 IKUS Software inc.
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
@@ -14,12 +14,10 @@
14
14
  # You should have received a copy of the GNU General Public License
15
15
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
 
17
- from urllib.parse import urljoin
18
-
19
17
  import cherrypy
20
18
 
21
19
 
22
- def url_for(*args, _relative=None, _base=None, **kwargs):
20
+ def url_for(*args, relative=None, **kwargs):
23
21
  """
24
22
  Generate a URL for the given endpoint/path (*args) with query params (**kwargs).
25
23
 
@@ -35,32 +33,34 @@ def url_for(*args, _relative=None, _base=None, **kwargs):
35
33
  - Integers are appended as path segments.
36
34
  - When path == "", existing request query parameters are merged (kwargs win).
37
35
  """
38
- # Handle query-string
39
- qs = [(k, v) for k, v in sorted(kwargs.items()) if v is not None]
40
-
41
- path = []
36
+ path = ""
42
37
  for chunk in args:
43
- if hasattr(chunk, '__url_for__') and callable(chunk.__url_for__):
44
- path.append(str(chunk.__url_for__()))
45
- elif hasattr(chunk, 'url_for'):
46
- path.append(str(chunk.url_for))
47
- elif isinstance(chunk, str):
48
- path.append(chunk)
38
+ if isinstance(chunk, str):
39
+ if not chunk.startswith('.'):
40
+ path += "/"
41
+ path += chunk.rstrip("/")
49
42
  elif isinstance(chunk, int):
50
- path.append(str(chunk))
43
+ path += "/"
44
+ path += str(chunk)
45
+ elif hasattr(chunk, '__url_for__') and callable(chunk.__url_for__):
46
+ path += "/"
47
+ path += str(chunk.__url_for__())
48
+ elif hasattr(chunk, 'url_for'):
49
+ path += "/"
50
+ path += str(chunk.url_for)
51
51
  else:
52
52
  raise ValueError('invalid positional arguments, url_for accept str, bytes, int: %r' % chunk)
53
- path = '/'.join(path)
54
53
  # When path is empty, we are browsing the same page.
55
54
  # Let keep the original query_string to avoid loosing it.
56
- if not path:
55
+ if path == "":
57
56
  params = cherrypy.request.params.copy()
58
57
  params.update(kwargs)
59
58
  qs = [(k, v) for k, v in sorted(params.items()) if v is not None]
60
- elif not path.startswith('.'):
61
- path = urljoin('/', path)
62
- # Outside a request, use cherrypy.tools.proxy config
63
- if not cherrypy.request.app and _base is None:
64
- _base = cherrypy.config.get('tools.proxy.base', None)
65
- # Use cherrypy to build the URL
66
- return cherrypy.url(path=path, qs=qs, relative=_relative, base=_base)
59
+ else:
60
+ qs = [(k, v) for k, v in sorted(kwargs.items()) if v is not None]
61
+ # Outside a request, use the external_url as base if defined
62
+ base = None
63
+ if not cherrypy.request.app:
64
+ cfg = cherrypy.tree.apps[''].cfg
65
+ base = cfg.external_url
66
+ return cherrypy.url(path=path, qs=qs, relative=relative, base=base)
@@ -1,5 +1,5 @@
1
- # Cherrypy-foundation
2
- # Copyright (C) 2020-2026 IKUS Software
1
+ # CherryPy Foundation
2
+ # Copyright (C) 2020-2025 IKUS Software
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: cherrypy-foundation
3
+ Version: 1.0.0a1
4
+ Summary: CherryPy Foundation
5
+ Author-email: Patrik Dufresne <patrik@ikus-soft.com>
6
+ License: GPLv3
7
+ Project-URL: Homepage, https://gitlab.com/ikus-soft/cherrypy-foundation
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Framework :: CherryPy
10
+ Classifier: Intended Audience :: System Administrators
11
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: <4,>=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE.md
18
+ Requires-Dist: babel
19
+ Requires-Dist: CherryPy
20
+ Requires-Dist: Jinja2
21
+ Requires-Dist: jinjax
22
+ Requires-Dist: pytz
23
+ Requires-Dist: WTForms>=3.2.1
24
+ Provides-Extra: test
25
+ Requires-Dist: ldap3; extra == "test"
26
+ Requires-Dist: apscheduler; extra == "test"
27
+ Requires-Dist: sqlalchemy; extra == "test"
28
+ Requires-Dist: parameterized; extra == "test"
29
+ Requires-Dist: pytest; extra == "test"
30
+ Requires-Dist: selenium; extra == "test"
31
+ Dynamic: license-file
32
+
33
+ # Cherrypy-foundation
34
+
35
+ <p align="center">
36
+ <a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-GPL--3.0-orange"></a>
37
+ <a href="https://gitlab.com/ikus-soft/cherrypy-foundation/pipelines"><img alt="Build" src="https://gitlab.com/ikus-soft/cherrypy-foundation/badges/master/pipeline.svg"></a>
38
+ <a href="https://sonar.ikus-soft.com/dashboard?id=cherrypy-foundation"><img alt="Quality Gate" src="https://sonar.ikus-soft.com/api/project_badges/measure?project=cherrypy-foundation&metric=alert_status"></a>
39
+ <a href="https://sonar.ikus-soft.com/dashboard?id=cherrypy-foundation"><img alt="Coverage" src="https://sonar.ikus-soft.com/api/project_badges/measure?project=cherrypy-foundation&metric=coverage"></a>
40
+ </p>
41
+
42
+ This project is a collection of common utilities for creating web applications with CherryPy. It provides integrations with SQLAlchemy, Jinja2, WTForms, and Bootstrap.
@@ -1,33 +1,29 @@
1
1
  cherrypy_foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- cherrypy_foundation/error_page.py,sha256=GEg_KZGRE63ZsTTtmrEpE9GsYcm6shZvp5KLR53sIMA,3248
3
- cherrypy_foundation/flash.py,sha256=vraKvmYmjdV1cGsPSxTLVsekwqOnoyiVPqKjv6UW5OU,1646
4
- cherrypy_foundation/form.py,sha256=8zp72fpYrmMGsIK5LxVY7sL_qqhoMDnIQoMND1s6CXk,3994
5
- cherrypy_foundation/logging.py,sha256=JyzcuAu3HWzvMTSFBuKpwgnJLz1lRlx-j6EkuTwlb6I,3463
6
- cherrypy_foundation/passwd.py,sha256=iyz3SgcvpgxsZgPUoTEXiqdKtwOANPrRKYhVPm2d_NQ,2139
7
- cherrypy_foundation/sessions.py,sha256=6OmjrJtX2Hhjhx5Q8Cy1nwG6_Ur6PwPErfPPA7FfzYg,3180
8
- cherrypy_foundation/url.py,sha256=WtfD0XEOs5FNmLvxhnBvi9aW4hQpUz0ZZXPfEXd4To8,2849
9
- cherrypy_foundation/widgets.py,sha256=-wxNv3wIgf7eB8WyyycfsqSCXep3YhZgBK_5PJ1qYbY,1549
10
- cherrypy_foundation/components/ColorModes.jinja,sha256=Ai2fy1qHFwEgutvyvvGjKJmffcBdNb7wmY20DJqZ8R4,3528
2
+ cherrypy_foundation/error_page.py,sha256=dhZBdVztS5tHr7jFsKUYWTvIUiZAfQ4PvXm5bkuKiTQ,3052
3
+ cherrypy_foundation/flash.py,sha256=fFRbutUX6c1lVHqjehmO9y98dJgmfNCjhd76t2mth2s,1542
4
+ cherrypy_foundation/form.py,sha256=8c9dO0o47sK3CBosTkGXoVRtzNQwY0aw0vNZfTqmhvo,3994
5
+ cherrypy_foundation/logging.py,sha256=YIOK5ZAZLCv52YDdP66yBYpEX1C336JnI3wnrTKl1Lw,3468
6
+ cherrypy_foundation/passwd.py,sha256=ZGdrBNKtLP75l01W6VEd8cIjSQ3guJ_YVPEfbSew7T0,2144
7
+ cherrypy_foundation/url.py,sha256=n12rU2R3VoPbGJ01-iXIMlZL4ohpVhk3vOc-__KkPzE,2814
8
+ cherrypy_foundation/widgets.py,sha256=0B5Y2V6x5Ufl6ExR3tc0Olrzj7N4TAAOtqGq_MUxBG0,1549
9
+ cherrypy_foundation/components/ColorModes.jinja,sha256=8MzkeZsra1wtIdiaQKc7UQUbMfRMUlmM6e9X7V1vfq0,3501
11
10
  cherrypy_foundation/components/Datatable.css,sha256=7wSwgdA61vYCdEuQ0bp2o0oSvu5mGLN1c6ovCUSe718,947
12
- cherrypy_foundation/components/Datatable.jinja,sha256=qUJyp8FCBdpH9J8kIvVzemRyyra0ushpZtTj5BhN9H8,3391
13
- cherrypy_foundation/components/Datatable.js,sha256=4qHj21GPRQzUBk9lWb3Y04EvuwnWmrENG7he5i91lgE,12072
11
+ cherrypy_foundation/components/Datatable.jinja,sha256=K-24vpN5TO8Jk2COvuC5uBaCj3PFLd6ubr28JHFq3KM,3375
12
+ cherrypy_foundation/components/Datatable.js,sha256=jiV78bJPNMTLcjrX03vaYcsEdRbI6ctzSvCo880H8Hw,12072
14
13
  cherrypy_foundation/components/Field.css,sha256=CtOkvIbix7ykrOKLJxQJLJsWfEwFqfducJ1BH2vlMvA,244
15
- cherrypy_foundation/components/Field.jinja,sha256=HuhZvHzv0MtFrcpoavbZHOLLdTdse8RcLIExXXx-Rsk,3319
14
+ cherrypy_foundation/components/Field.jinja,sha256=QYJdB9xRysQrdC_oDlu9V8cpZyFJxQOlnZOi_wt-iP8,2762
16
15
  cherrypy_foundation/components/Field.js,sha256=SFixZ62WlLq7SSCEazMAGhSnc9EnQ1wg6PZX4ayO6ZE,2047
17
- cherrypy_foundation/components/Fields.jinja,sha256=_8Or6DOlciKjRao-sdBpLs--bx5V-K80X3hDEA1VKCQ,165
18
- cherrypy_foundation/components/Flash.jinja,sha256=COy44drQsXpbZajjJS4w_9NMPYFdMfUNdfhd7SbFdYA,442
16
+ cherrypy_foundation/components/Fields.jinja,sha256=UDu1txwMguvr7dyga9PXepEhFvz9C6ZvchBnhQCWgHI,318
19
17
  cherrypy_foundation/components/Icon.jinja,sha256=Z1RGYBg5xlDEoUy3glqb_k_LEjkJHeCxQXqDEvWzEF4,135
20
- cherrypy_foundation/components/LocaleSelection.jinja,sha256=NHhQGI6CCJl4LWl-eteu_AIg1_qc1MusuA1fKcrjluQ,509
21
- cherrypy_foundation/components/LocaleSelection.js,sha256=-iTZD5ejg1R9F3tZASTH7dkgd19upke-pzOhsZ9K_LA,670
22
18
  cherrypy_foundation/components/SideBySideMultiSelect.css,sha256=_poMY9O8rvDsOh01pQLf9qtg1Gm4eCM2HsM_ekC5zkk,503
23
19
  cherrypy_foundation/components/SideBySideMultiSelect.jinja,sha256=sud1WP-6JzuP7ZLRr-JQqvgMRWZRlXvxUJfguFr_klk,478
24
20
  cherrypy_foundation/components/SideBySideMultiSelect.js,sha256=5YMz1pgkXeWC_SRRfDbQI3X-c4PuxiTIpbWJt9sY7Rc,197
25
- cherrypy_foundation/components/Typeahead.css,sha256=iaLKi4lx01MQqSSRrdDUIDCCD6Q_0INCsMRZ3nqdkuw,1718
26
- cherrypy_foundation/components/Typeahead.jinja,sha256=TjCotrTIzSx7TujxmgNxLIgxvtequCARLCjqeDzlZXU,3218
21
+ cherrypy_foundation/components/Typeahead.css,sha256=8VyPN6FP_l0_mRyES2tjrwr9rEkKT4y0obeBI6nfGLk,1645
22
+ cherrypy_foundation/components/Typeahead.jinja,sha256=g7oiiROxIjwkgwiZtxxFlSIIaWUr2ml2eS0RWm7yUvU,3215
27
23
  cherrypy_foundation/components/Typeahead.js,sha256=mnsPRwM_1xtI7ImGn_pu46QdFw_LK_PDHNCRKp_CVxc,236
28
- cherrypy_foundation/components/__init__.py,sha256=rd202TEmfnwBVD2xkJA2NMKuYeUSAPmBR_EBdHeVJLg,2007
24
+ cherrypy_foundation/components/__init__.py,sha256=15gQpb1eSzMmPPS5Gna8E25DGN0Midrxdns7veFKOyU,2034
29
25
  cherrypy_foundation/components/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- cherrypy_foundation/components/tests/test_static.py,sha256=ELbyEs2Kd-UYj7b3dvrIBa8vHwanB7XRAceS3knYq6c,3814
26
+ cherrypy_foundation/components/tests/test_static.py,sha256=bDtS0CFM5LZxwxbXbUyLkXv7sM5KM-eQtHOWywZu_nc,3814
31
27
  cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.css,sha256=AEMichyFVzMXWbxt2qy7aJsPBxXWiK7IK9BW0tW1zDs,99556
32
28
  cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.min.css,sha256=pdY4ejLKO67E0CM2tbPtq1DJ3VGDVVdqAR6j3ZwdiE4,87008
33
29
  cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff,sha256=9VUTt7WRy4SjuH_w406iTUgx1v7cIuVLkRymS1tUShU,180288
@@ -83,54 +79,39 @@ cherrypy_foundation/components/vendor/popper/popper.min.js,sha256=whL0tQWoY1Ku1i
83
79
  cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.css,sha256=gBsvk2nlQnvCXdG7JITz1SrnqWQrO6EsUo_r06-KRtU,12464
84
80
  cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.js,sha256=eFBtgouYdW587kYINWsjkN-UR8GVWAG_fQe1fpJfjOw,47828
85
81
  cherrypy_foundation/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
- cherrypy_foundation/plugins/db.py,sha256=S2OqujZ-D_TxUM6NbQW7XGDt8eGDup1Wbhf0XCcYSbs,10108
87
- cherrypy_foundation/plugins/ldap.py,sha256=Y0gUD5CcFaUAI7SSPIlrlr4IkQ1QePX2--qfZrIBlbo,9861
88
- cherrypy_foundation/plugins/restapi.py,sha256=1W1mmVh-rIzg_mcFaNQZZXpqF7eN7VhRL9oTEiQOr-w,2843
89
- cherrypy_foundation/plugins/scheduler.py,sha256=m2XLM7euRjC5bkU4NILvwnRo0w6NWRwmCI19iEiQlE0,10777
90
- cherrypy_foundation/plugins/smtp.py,sha256=7qZv0VrQxi7gNoVdRds0F6Ya9pyo3LlZ12uFaVpAsms,7658
82
+ cherrypy_foundation/plugins/db.py,sha256=V-gk6Zg-DRNA6snfHLte3ye6hzowvuE-8lYKCTKs7Vg,9118
83
+ cherrypy_foundation/plugins/ldap.py,sha256=MTg-TqdyziuqFYr-wXecHvu0zK7YCzr-k5E8jq3dF-k,9519
84
+ cherrypy_foundation/plugins/restapi.py,sha256=S5GIxHL0M3Wcs33fyVOb0RfX1FXbp2fqUxJ_ufqBrD4,2843
85
+ cherrypy_foundation/plugins/scheduler.py,sha256=aUk_X-z1zLD3oqfuMdm6AEV5j6Iyy422y7sMn3SrYVg,5467
86
+ cherrypy_foundation/plugins/smtp.py,sha256=IimRsULIEEpwI8OcYAxNifwkjdQNBNX2YtrIu1635fQ,6789
91
87
  cherrypy_foundation/plugins/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
- cherrypy_foundation/plugins/tests/test_db.py,sha256=r2kPdYJOu7mqnUBKrQDSkHamHJZhJibW-sxJl5o-MJs,3921
93
- cherrypy_foundation/plugins/tests/test_ldap.py,sha256=NysUplX-E8k1IHWPPnJxiHwLdNhHqYUUU2ZDkfDx2s8,17269
94
- cherrypy_foundation/plugins/tests/test_scheduler.py,sha256=uFLXKisV7lCSHKE-AurE7r6chxqcVm_OCQL8RrndH94,3564
95
- cherrypy_foundation/plugins/tests/test_scheduler_db.py,sha256=_jw1usLhQlV0aoMbTtdJMBQypA5eSCWmOOsFvfM2zJQ,3101
96
- cherrypy_foundation/plugins/tests/test_smtp.py,sha256=LYUXjDe8TtyruBSm18N73nSxHidBCIb1Om-9zeG56I4,5462
97
- cherrypy_foundation/tests/__init__.py,sha256=bbdyalzM2mwBbId-7JBPXYuRzk7oUBCncq33ypD9v3Y,2818
98
- cherrypy_foundation/tests/test_error_page.py,sha256=EmsCXYkFFMReNSORZYCo7qKvL2x98QgtKIlv1nFKIUU,2399
99
- cherrypy_foundation/tests/test_flash.py,sha256=NNbT3XI1dHk0hXqJsWndCmPPohK9ig87ZYJaRNPJxR8,1994
100
- cherrypy_foundation/tests/test_form.py,sha256=-2h38e00hWlYNJeVWrUB2V11UZ3x0FmTcsCez5vWoas,5542
101
- cherrypy_foundation/tests/test_logging.py,sha256=Lg1yGwFHTSpGbPvNXBi9risypZziJlAOMi1DeennxRs,2623
102
- cherrypy_foundation/tests/test_passwd.py,sha256=nGqJZYwRPziifLvf5rm3YifxAvIOFdU_6LkHaUvRk8w,1951
103
- cherrypy_foundation/tests/test_sessions.py,sha256=_jcCn75hMGr7ETucWZnCqWcI3pw5PCdlzPQuxdE4yZE,2873
104
- cherrypy_foundation/tests/test_url.py,sha256=sSNwK7KZJcrLtnf_XROYQAPQoYNO5lAsa2BjhFjgmws,6434
105
- cherrypy_foundation/tests/templates/test_flash.html,sha256=b1S4I9v0n-Y1yoTUh2ZKNysR1NMrqv8ldvqONtmInzw,213
106
- cherrypy_foundation/tests/templates/test_form.html,sha256=liubTm2q74-3hqQb4weaGJU3sq4vdq868GdVahBafSQ,333
107
- cherrypy_foundation/tests/templates/test_url.html,sha256=xoyRIkIzzM8Q-M39i80P4j7Wo8Lkzh1rIzRq-40Szak,528
88
+ cherrypy_foundation/plugins/tests/test_db.py,sha256=TfI4JIWBZzzXE1Y_HceoQ4c4OY_Ff-CgW--Z0_oxRO8,3925
89
+ cherrypy_foundation/plugins/tests/test_ldap.py,sha256=7EhFvhxwDCCBoNlAD5XpZxLtKvkrjpQxrtCHMT9wkQ4,14393
90
+ cherrypy_foundation/plugins/tests/test_scheduler.py,sha256=NCk0rqggOxj9RdKtVxU6EosGk_lOEiCqLXW9or6hovM,3054
91
+ cherrypy_foundation/plugins/tests/test_smtp.py,sha256=J06pZTS2SPXpOvEDand7_xfkadO7vXSpAACyPre1rZA,3918
92
+ cherrypy_foundation/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
+ cherrypy_foundation/tests/test_error_page.py,sha256=8u5p6lv_I4XvfGipjJXNFwW7G1N8AK8IOnc_Th-Ebto,2181
94
+ cherrypy_foundation/tests/test_passwd.py,sha256=gC5O4yhHyU1YRYuDc0pG0T_5zvrG2qrr6P822iyK3Rg,1956
108
95
  cherrypy_foundation/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
- cherrypy_foundation/tools/auth.py,sha256=WRXiZdRXJcd4uZFDb8DAGX-ySqiYytdWyjiQiLZb9Vo,9935
110
- cherrypy_foundation/tools/auth_mfa.py,sha256=hDwfKc2zFY3bxOm1JRLLFQRXVPL9atBQ-PEwQTEQUww,9579
111
- cherrypy_foundation/tools/i18n.py,sha256=f4m9fc2FKtcdUpXgnLXzhoipn4oLDuHY94Vv3KPSfp0,17126
112
- cherrypy_foundation/tools/jinja2.py,sha256=hTiOepdb7kyDEqCK4NH2O-v1lrgPGNX4VV3AWZ-OsAc,5967
113
- cherrypy_foundation/tools/ratelimit.py,sha256=C_bGzkx9waIJlpK-X6_SH8r1rP0I_xr0wLnGZhSEGEo,8742
114
- cherrypy_foundation/tools/secure_headers.py,sha256=XqmjU5MCK8fTacQ2uTUiODYN4uaa9cCGfQJZVnp4J4g,3921
115
- cherrypy_foundation/tools/sessions_timeout.py,sha256=M54IpRc3c4rUH0Cbri363O6GhD89EmzrjqvWeul8khc,7484
96
+ cherrypy_foundation/tools/auth.py,sha256=F9SMQMTOqHbtDw13VdlGf9gYJtUhXrr9p-RYPkcEySs,9090
97
+ cherrypy_foundation/tools/auth_mfa.py,sha256=2TU_EnykYRmj1pluGHFUFGzrE0jkyekamECf9TUd8HQ,9250
98
+ cherrypy_foundation/tools/errors.py,sha256=DhWl03tthV9tlN_3qdJzgWg6LQS7ANP5gCl6R_VgW4E,1027
99
+ cherrypy_foundation/tools/i18n.py,sha256=pZVawC8ahzvm1HmMe_6zZuYTjsWW7OsKdFjV0KtF6fk,13885
100
+ cherrypy_foundation/tools/jinja2.py,sha256=lZ3aiYerop9WDecrsX9B9OY49NmH-eIJZiXVnVqDY2I,5290
101
+ cherrypy_foundation/tools/ratelimit.py,sha256=k0MRdanioF8UNRHcFUWyT5aVvHtSZ1-BlVYtEtLysmI,8386
102
+ cherrypy_foundation/tools/secure_headers.py,sha256=PMKwL622sbBtjRnq-weZH_OewuaG4cR2jqIr1DOUNXM,3994
103
+ cherrypy_foundation/tools/sessions_timeout.py,sha256=6iBWJntPMk_Qt94fBSfBISf1IXInSh-1XrxLbKXFV-g,7408
116
104
  cherrypy_foundation/tools/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
- cherrypy_foundation/tools/tests/test_auth.py,sha256=qQxCs8uCgu3C2Of9DRWplpCLLB7zSiJkrV4F9muLkJg,3414
118
- cherrypy_foundation/tools/tests/test_auth_mfa.py,sha256=qYIlw4tDQUi9R7nPwph6UpMsThWDN1EYW-FrrOF_grQ,15111
119
- cherrypy_foundation/tools/tests/test_i18n.py,sha256=6lHV3kxriRI5jo-YMnxzKKDFtLhogUrqK7D4X2-m42U,10758
120
- cherrypy_foundation/tools/tests/test_jinja2.py,sha256=3r96jcm68Jtw_hGwpQOlT8Jao6upPSnwENaScluL-7E,5545
121
- cherrypy_foundation/tools/tests/test_ratelimit.py,sha256=BZn2lhbuaVeIPq80eUn-SFu0mMN3Mb4ttwVoXbozWUA,3431
122
- cherrypy_foundation/tools/tests/test_secure_headers.py,sha256=Iu3X7u6VJpWOmuNMv1qZFh0oPWdLZAdoJppRyuTV148,7769
123
- cherrypy_foundation/tools/tests/components/Button.jinja,sha256=uSLp1GpEIgZNXK_GWglu0E_a1c3jHpDLI66MRfMqGhE,95
105
+ cherrypy_foundation/tools/tests/test_auth.py,sha256=YA8mh9w__8z65LMjiVASCPpcTdLUTanzAwxztIkoFI0,2851
106
+ cherrypy_foundation/tools/tests/test_i18n.py,sha256=-bJuKn8PHHidBWhoFSVr4PdX96m3A50YY4i5osyU7DY,6251
107
+ cherrypy_foundation/tools/tests/test_ratelimit.py,sha256=rrqybwMbh1GFlF2-Ut57zHPAc1uqX88aqea6VS_6p5E,3449
124
108
  cherrypy_foundation/tools/tests/locales/messages.pot,sha256=5K9piTRL7H5MxDXFIWJsCccSJRA0HwfCQQU8b8VYo30,40
125
- cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.mo,sha256=bsJTVL4OefevkxeHDS3VcW3egP6Yq18LFXwjSyoqIng,336
126
- cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.po,sha256=glzYY96jmCaGyxYtMqmNF-1q-OYWXBIkMysvbXO4L6E,351
109
+ cherrypy_foundation/tools/tests/locales/en/LC_MESSAGES/messages.mo,sha256=cdyG2Js1TIU6eenDX1ICH8uP45yvl0OLN0-SMUXTBa4,259
110
+ cherrypy_foundation/tools/tests/locales/en/LC_MESSAGES/messages.po,sha256=JCpiRLLHUSYQhzta8ZYjfB50NmpwPGNCTNwo2Glww14,322
127
111
  cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.mo,sha256=u3_kl_nqZ3FNaSyKVQKmu4KJzN3xOxxJNVmcdhw37jA,327
128
112
  cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po,sha256=6_Sk9Igqm7dtpyyS701p5qc4DvOJE7TRT0ajRZctFAQ,342
129
- cherrypy_foundation/tools/tests/templates/test_jinja2.html,sha256=s1bHmy-lyf0YW0t-LOx3ugILV2kqFoBNYxziWgrZbo0,216
130
- cherrypy_foundation/tools/tests/templates/test_jinjax.html,sha256=NImzIW0mUHxilFd61PSoxFC-yu1nayEVwv-5zlgD9yo,179
131
- cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html,sha256=yre8j7HBjpTQZHpM0PuB3ASGD3O4vKkJ-y72Fm6STgY,771
132
- cherrypy_foundation-1.0.0.dist-info/licenses/LICENSE.md,sha256=trSLYs5qlaow_bBwsLTRKpmTXsXzFksM_YUCMqrgAJQ,35149
133
- cherrypy_foundation-1.0.0.dist-info/METADATA,sha256=MdVr55OyRUTYuG2Ly-LXBSf6e1zS7Jxb8n3GE2igpCA,3548
134
- cherrypy_foundation-1.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
135
- cherrypy_foundation-1.0.0.dist-info/top_level.txt,sha256=B1vQPTLYhpKJ6W0JkRCWyAf8RPcnwJWdYxixv75-4ew,20
136
- cherrypy_foundation-1.0.0.dist-info/RECORD,,
113
+ cherrypy_foundation-1.0.0a1.dist-info/licenses/LICENSE.md,sha256=trSLYs5qlaow_bBwsLTRKpmTXsXzFksM_YUCMqrgAJQ,35149
114
+ cherrypy_foundation-1.0.0a1.dist-info/METADATA,sha256=bQ3It-tIMNYHnif8OInatJvVDOyHONe-6uXE9cFVevI,2022
115
+ cherrypy_foundation-1.0.0a1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
116
+ cherrypy_foundation-1.0.0a1.dist-info/top_level.txt,sha256=B1vQPTLYhpKJ6W0JkRCWyAf8RPcnwJWdYxixv75-4ew,20
117
+ cherrypy_foundation-1.0.0a1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.2)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,13 +0,0 @@
1
- {# def messages, floating=False #}
2
- <div {{ attrs.render(class="flash-messages") }}>
3
- {% for message, level in messages %}
4
- <div class="alert alert-{{'danger' if level == 'error' else level }} alert-dismissible fade show"
5
- role="alert">
6
- {{ message }}
7
- <button type="button"
8
- class="btn-close"
9
- data-bs-dismiss="alert"
10
- aria-label="Close"></button>
11
- </div>
12
- {% endfor %}
13
- </div>
@@ -1,13 +0,0 @@
1
- {# def header="Language selection" #}
2
- <li>
3
- <h6 class="dropdown-header">{{ header }}</h6>
4
- </li>
5
- {% set cur_locale = get_translation().locale %}
6
- {% for locale in list_available_locales() %}
7
- <li>
8
- <button aria-pressed="{{ 'true' if cur_locale == locale else 'false' }}"
9
- class="dropdown-item btn-locale{{ ' active' if cur_locale == locale else '' }}"
10
- data-locale="{{ locale.language }}"
11
- type="button">{{ locale.display_name.capitalize() }}</button>
12
- </li>
13
- {% endfor %}
@@ -1,26 +0,0 @@
1
- /*
2
- * Handle locale selection.
3
- */
4
- document.addEventListener('DOMContentLoaded', function () {
5
- const LOCALE_COOKIE_NAME = 'locale';
6
- const ONE_YEAR = 60 * 60 * 24 * 365;
7
-
8
- function setLocaleCookie(locale) {
9
- document.cookie = `${LOCALE_COOKIE_NAME}=${locale}; path=/; max-age=${ONE_YEAR}; SameSite=Lax`;
10
- }
11
-
12
- function onLanguageClick(event) {
13
- const locale = event.target.dataset.locale;
14
- if (!locale) return;
15
-
16
- setLocaleCookie(locale);
17
-
18
- // Reload page so backend can use the new language
19
- window.location.reload();
20
- }
21
-
22
- document.querySelectorAll('.btn-locale').forEach(btn => {
23
- btn.addEventListener('click', onLanguageClick);
24
- });
25
-
26
- });
@@ -1,107 +0,0 @@
1
- # Scheduler plugins for Cherrypy
2
- # Copyright (C) 2026 IKUS Software
3
- #
4
- # This program is free software: you can redistribute it and/or modify
5
- # it under the terms of the GNU General Public License as published by
6
- # the Free Software Foundation, either version 3 of the License, or
7
- # (at your option) any later version.
8
- #
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU General Public License
15
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
- import importlib
17
- import tempfile
18
- from threading import Event
19
-
20
- import cherrypy
21
- from cherrypy.test import helper
22
-
23
- from .. import scheduler # noqa
24
-
25
- done = Event()
26
-
27
- HAS_SQLALCHEMY = importlib.util.find_spec("sqlalchemy") is not None
28
- if not HAS_SQLALCHEMY:
29
- pass
30
- else:
31
- from sqlalchemy import Column, Integer, String
32
- from sqlalchemy.exc import IntegrityError
33
-
34
- from .. import db # noqa
35
-
36
- Base = cherrypy.db.get_base()
37
-
38
- class User2(Base):
39
- __tablename__ = 'users2'
40
- id = Column(Integer, primary_key=True)
41
- username = Column(String)
42
-
43
- def __repr__(self):
44
- return f"User2(id={self.id}, username='{self.username}')"
45
-
46
- class Root:
47
-
48
- @cherrypy.expose
49
- def index(self):
50
- return str(User2.query.all())
51
-
52
- @cherrypy.expose
53
- def add(self, username):
54
- try:
55
- User2(username=username).add().commit()
56
- return "OK"
57
- except IntegrityError as e:
58
- return str(e)
59
-
60
- def create_user(*args, **kwargs):
61
- user = User2(*args, **kwargs).add()
62
- user.commit()
63
- done.set()
64
-
65
-
66
- class DbSchedulerPluginTest(helper.CPWebCase):
67
- interactive = False
68
-
69
- @classmethod
70
- def setup_class(cls):
71
- cls.tempdir = tempfile.TemporaryDirectory(prefix='cherrypy-foundation-', suffix='-db-test')
72
- super().setup_class()
73
-
74
- @classmethod
75
- def teardown_class(cls):
76
- cls.tempdir.cleanup()
77
- super().teardown_class()
78
-
79
- @classmethod
80
- def setup_server(cls):
81
- cherrypy.config.update(
82
- {
83
- 'db.uri': f"sqlite:///{cls.tempdir.name}/data.db",
84
- }
85
- )
86
- cherrypy.tree.mount(Root(), '/')
87
-
88
- def setUp(self) -> None:
89
- done.clear()
90
- cherrypy.db.create_all()
91
- return super().setUp()
92
-
93
- def tearDown(self):
94
- cherrypy.db.drop_all()
95
- return super().tearDown()
96
-
97
- def test_add_job_now(self):
98
- # Given a task
99
- # When scheduling that task
100
- scheduled = cherrypy.engine.publish('scheduler:add_job_now', create_user, username='myuser')
101
- self.assertTrue(scheduled)
102
- # When waiting for all tasks
103
- cherrypy.scheduler.wait_for_jobs()
104
- # Then the task get called
105
- self.assertTrue(done.is_set())
106
- # Then database was updated
107
- User2.query.filter(User2.username == 'myuser').one()
@@ -1,93 +0,0 @@
1
- # Cherrypy-foundation
2
- # Copyright (C) 2025-2026 IKUS Software
3
- #
4
- # This program is free software: you can redistribute it and/or modify
5
- # it under the terms of the GNU General Public License as published by
6
- # the Free Software Foundation, either version 3 of the License, or
7
- # (at your option) any later version.
8
- #
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU General Public License
15
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
-
17
-
18
- import logging
19
- import os
20
- import time
21
- from contextlib import contextmanager
22
-
23
- import cherrypy
24
- import zc.lockfile
25
- from cherrypy.lib import locking
26
- from cherrypy.lib.sessions import FileSession as CPFileSession
27
-
28
-
29
- class FileSession(CPFileSession):
30
- """
31
- Override implementation of cherrpy file session to improve file locking.
32
- """
33
-
34
- def acquire_lock(self, path=None):
35
- """Acquire an exclusive lock on the currently-loaded session data."""
36
- # See Issue https://github.com/cherrypy/cherrypy/issues/2065
37
-
38
- if path is None:
39
- path = self._get_file_path()
40
- path += self.LOCK_SUFFIX
41
- checker = locking.LockChecker(self.id, self.lock_timeout)
42
- while not checker.expired():
43
- try:
44
- self.lock = zc.lockfile.LockFile(path)
45
- except zc.lockfile.LockError:
46
- # Sleep for 1ms only.
47
- time.sleep(0.001)
48
- else:
49
- break
50
- self.locked = True
51
- if self.debug:
52
- cherrypy.log('Lock acquired.', 'TOOLS.SESSIONS')
53
-
54
- def clean_up(self):
55
- """Also clean-up left over lock files."""
56
- # See Issue https://github.com/cherrypy/cherrypy/issues/1855
57
-
58
- # Clean-up session files.
59
- CPFileSession.clean_up(self)
60
-
61
- # Then clean-up any orphane lock files.
62
- suffix_len = len(self.LOCK_SUFFIX)
63
- files = os.listdir(self.storage_path)
64
- lock_files = [
65
- fname for fname in files if fname.startswith(self.SESSION_PREFIX) and fname.endswith(self.LOCK_SUFFIX)
66
- ]
67
- for fname in lock_files:
68
- session_file = fname[:-suffix_len]
69
- if session_file not in files:
70
- filepath = os.path.join(self.storage_path, fname)
71
- try:
72
- os.unlink(filepath)
73
- except Exception as e:
74
- cherrypy.log(f'Error deleting {filepath}: {e}', 'TOOLS.SESSIONS', severity=logging.WARNING)
75
-
76
-
77
- @contextmanager
78
- def session_lock():
79
- """
80
- Acquire session lock as required. Support re-intrant lock.
81
- """
82
- s = cherrypy.serving.session
83
- if s.locking == 'explicit' and not s.locked:
84
- s.acquire_lock()
85
- try:
86
- yield s
87
- finally:
88
- # When explicit, we want to save the session (with also release the lock.)
89
- if s.locking == 'explicit':
90
- s.save()
91
- cherrypy.serving.request._sessionsaved = True
92
- else:
93
- yield s
@@ -1,9 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <title>test-flash</title>
5
- </head>
6
- <body>
7
- <Flash messages={{ get_flashed_messages() }} class="mb-2" style="min-height:100px"/>
8
- </body>
9
- </html>
@@ -1,16 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <title>test-form</title>
5
- </head>
6
- <body>
7
- {% if form.error_message %}
8
- <p>
9
- {{ form.error_message }}
10
- </p>
11
- {% endif %}
12
- <form method="post" action="/">
13
- <Fields form={{ form }} />
14
- </form>
15
- </body>
16
- </html>
@@ -1,15 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <title>test-url</title>
5
- </head>
6
- <body>
7
- Empty: {{ url_for("", **kwargs) }}<br/>
8
- Dot: {{ url_for(".", **kwargs) }}<br/>
9
- Dot page: {{ url_for(".", "my-page", **kwargs) }}<br/>
10
- Slash: {{ url_for("/", **kwargs) }}<br/>
11
- Page: {{ url_for("my-page", **kwargs) }}<br/>
12
- Slash page: {{ url_for("/my-page", **kwargs) }}<br/>
13
- Query: {{ url_for("my-page", foo='1', bar='test with space', **kwargs) }}<br/>
14
- </body>
15
- </html>
@@ -1,61 +0,0 @@
1
- # Cherrypy-foundation
2
- # Copyright (C) 2026 IKUS Software
3
- #
4
- # This program is free software: you can redistribute it and/or modify
5
- # it under the terms of the GNU General Public License as published by
6
- # the Free Software Foundation, either version 3 of the License, or
7
- # (at your option) any later version.
8
- #
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU General Public License
15
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
- import importlib
17
- from unittest import skipUnless
18
-
19
- import cherrypy
20
- from cherrypy.test import helper
21
-
22
- import cherrypy_foundation.tools.jinja2 # noqa
23
- from cherrypy_foundation.flash import flash, get_flashed_messages
24
-
25
- HAS_JINJAX = importlib.util.find_spec("jinjax") is not None
26
-
27
- env = cherrypy.tools.jinja2.create_env(
28
- package_name=__package__,
29
- globals={'get_flashed_messages': get_flashed_messages},
30
- )
31
-
32
-
33
- @cherrypy.tools.sessions(locking='explicit')
34
- @cherrypy.tools.jinja2(env=env)
35
- class Root:
36
-
37
- @cherrypy.expose
38
- @cherrypy.tools.jinja2(template='test_flash.html')
39
- def index(self):
40
- flash('default flash message', level='info')
41
- return {}
42
-
43
-
44
- @skipUnless(HAS_JINJAX, reason='Required jinjax')
45
- class FormTest(helper.CPWebCase):
46
- default_lang = None
47
- interactive = False
48
-
49
- @classmethod
50
- def setup_server(cls):
51
- cherrypy.tree.mount(Root(), '/')
52
-
53
- def test_get_flash(self):
54
- # Given a page returning a flash message
55
- # When querying the page that include this form
56
- self.getPage("/")
57
- self.assertStatus(200)
58
- # Then page display the message.
59
- self.assertInBody('test-flash')
60
- self.assertInBody('<div class="alert alert-info alert-dismissible fade show"')
61
- self.assertInBody('default flash message')