cherrypy-foundation 1.0.0a10__py3-none-any.whl → 1.0.0a11__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.
- cherrypy_foundation/plugins/ldap.py +4 -2
- cherrypy_foundation/plugins/scheduler.py +13 -2
- cherrypy_foundation/plugins/tests/test_scheduler_db.py +107 -0
- cherrypy_foundation/tools/i18n.py +7 -4
- cherrypy_foundation/tools/tests/test_i18n.py +1 -1
- {cherrypy_foundation-1.0.0a10.dist-info → cherrypy_foundation-1.0.0a11.dist-info}/METADATA +1 -1
- {cherrypy_foundation-1.0.0a10.dist-info → cherrypy_foundation-1.0.0a11.dist-info}/RECORD +10 -9
- {cherrypy_foundation-1.0.0a10.dist-info → cherrypy_foundation-1.0.0a11.dist-info}/WHEEL +0 -0
- {cherrypy_foundation-1.0.0a10.dist-info → cherrypy_foundation-1.0.0a11.dist-info}/licenses/LICENSE.md +0 -0
- {cherrypy_foundation-1.0.0a10.dist-info → cherrypy_foundation-1.0.0a11.dist-info}/top_level.txt +0 -0
|
@@ -26,7 +26,7 @@ _safe = ldap3.utils.conv.escape_filter_chars
|
|
|
26
26
|
|
|
27
27
|
def all_attribute(attributes, keys, default=None):
|
|
28
28
|
"""
|
|
29
|
-
Extract
|
|
29
|
+
Extract all values from LDAP attributes.
|
|
30
30
|
"""
|
|
31
31
|
# Skip loopkup if key is not defined.
|
|
32
32
|
if not keys:
|
|
@@ -61,7 +61,9 @@ def first_attribute(attributes, keys, default=None):
|
|
|
61
61
|
for attr in keys:
|
|
62
62
|
try:
|
|
63
63
|
value = attributes[attr]
|
|
64
|
-
if isinstance(value, list)
|
|
64
|
+
if isinstance(value, list):
|
|
65
|
+
if len(value) == 0:
|
|
66
|
+
pass
|
|
65
67
|
return value[0]
|
|
66
68
|
else:
|
|
67
69
|
return value
|
|
@@ -35,7 +35,6 @@ def clear_db_sessions(func):
|
|
|
35
35
|
|
|
36
36
|
@wraps(func)
|
|
37
37
|
def func_wrapper(*args, **kwargs):
|
|
38
|
-
cherrypy.db.clear_sessions()
|
|
39
38
|
try:
|
|
40
39
|
result = func(*args, **kwargs)
|
|
41
40
|
finally:
|
|
@@ -206,6 +205,7 @@ class Scheduler(SimplePlugin):
|
|
|
206
205
|
"""
|
|
207
206
|
hour, minute = execution_time.split(':', 2)
|
|
208
207
|
return self.add_job(
|
|
208
|
+
id=getattr(func, '__name__', str(func)),
|
|
209
209
|
func=func,
|
|
210
210
|
name=getattr(func, '__name__', str(func)),
|
|
211
211
|
args=args,
|
|
@@ -214,6 +214,8 @@ class Scheduler(SimplePlugin):
|
|
|
214
214
|
hour=hour,
|
|
215
215
|
minute=minute,
|
|
216
216
|
misfire_grace_time=None,
|
|
217
|
+
coalesce=True,
|
|
218
|
+
replace_existing=True,
|
|
217
219
|
)
|
|
218
220
|
|
|
219
221
|
def add_job_now(self, func, *args, **kwargs):
|
|
@@ -243,20 +245,29 @@ class Scheduler(SimplePlugin):
|
|
|
243
245
|
Remove the given job from scheduler.
|
|
244
246
|
"""
|
|
245
247
|
# Search for a matching job
|
|
248
|
+
return_value = False
|
|
249
|
+
if self._scheduler is None:
|
|
250
|
+
return return_value
|
|
246
251
|
for j in self._scheduler.get_jobs(jobstore=jobstore):
|
|
247
|
-
if j.func == job:
|
|
252
|
+
if j.func == job or j.name == job:
|
|
248
253
|
self._scheduler.remove_job(job_id=j.id, jobstore=jobstore)
|
|
254
|
+
return_value = True
|
|
255
|
+
return return_value
|
|
249
256
|
|
|
250
257
|
def remove_all_jobs(self, jobstore=None):
|
|
251
258
|
"""
|
|
252
259
|
Remove all jobs from scheduler.
|
|
253
260
|
"""
|
|
261
|
+
if self._scheduler is None:
|
|
262
|
+
return
|
|
254
263
|
self._scheduler.remove_all_jobs(jobstore=jobstore)
|
|
255
264
|
|
|
256
265
|
def wait_for_jobs(self, jobstore=None):
|
|
257
266
|
"""
|
|
258
267
|
Used to wait for all running jobs to complete.
|
|
259
268
|
"""
|
|
269
|
+
if self._scheduler is None:
|
|
270
|
+
return
|
|
260
271
|
# Wait until the queue is empty.
|
|
261
272
|
while any(
|
|
262
273
|
job for job in self._scheduler.get_jobs(jobstore=jobstore) if job.next_run_time < datetime.now(timezone.utc)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Scheduler plugins for Cherrypy
|
|
2
|
+
# Copyright (C) 2025 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()
|
|
@@ -186,10 +186,13 @@ def _search_translation(dirname, domain, *locales):
|
|
|
186
186
|
# Assign prefered local to this translation to know the current locale.
|
|
187
187
|
trans_locale = Locale.parse(t.files[0].split('/')[-3])
|
|
188
188
|
for locale in locales:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
try:
|
|
190
|
+
locale = Locale.parse(locale)
|
|
191
|
+
if trans_locale == locale or trans_locale.language == locale.language:
|
|
192
|
+
t.locale = locale
|
|
193
|
+
break
|
|
194
|
+
except ValueError:
|
|
195
|
+
continue # Invalid locale identifier
|
|
193
196
|
return t
|
|
194
197
|
|
|
195
198
|
|
|
@@ -150,7 +150,7 @@ class TestI18nWebCase(AbstractI18nTest):
|
|
|
150
150
|
with i18n.preferred_lang('de_CH'):
|
|
151
151
|
self.assertEqual('29. März 2023, 17:40:11 UTC', i18n.format_datetime(date, format='long'))
|
|
152
152
|
|
|
153
|
-
with i18n.preferred_timezone('
|
|
153
|
+
with i18n.preferred_timezone('Europe/Paris'):
|
|
154
154
|
with i18n.preferred_lang('fr'):
|
|
155
155
|
self.assertEqual('29 mars 2023, 19:40:11 +0200', i18n.format_datetime(date, format='long'))
|
|
156
156
|
with i18n.preferred_lang('fr_CH'):
|
|
@@ -81,14 +81,15 @@ cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.css,sha256=
|
|
|
81
81
|
cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.js,sha256=eFBtgouYdW587kYINWsjkN-UR8GVWAG_fQe1fpJfjOw,47828
|
|
82
82
|
cherrypy_foundation/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
83
|
cherrypy_foundation/plugins/db.py,sha256=46S-2kR9BHVYRN9cxWwYfzY2WkpEVBCpx1ubsJolYfA,10108
|
|
84
|
-
cherrypy_foundation/plugins/ldap.py,sha256=
|
|
84
|
+
cherrypy_foundation/plugins/ldap.py,sha256=hehNXGYWLSh_tqqbI_sZdFGjC9uJAyeZ-vMNPai9s_w,9909
|
|
85
85
|
cherrypy_foundation/plugins/restapi.py,sha256=S5GIxHL0M3Wcs33fyVOb0RfX1FXbp2fqUxJ_ufqBrD4,2843
|
|
86
|
-
cherrypy_foundation/plugins/scheduler.py,sha256=
|
|
86
|
+
cherrypy_foundation/plugins/scheduler.py,sha256=McTR72GGTuW7UZKco_Czmp09PfqJ-aHXJxh7h9BFZvA,10777
|
|
87
87
|
cherrypy_foundation/plugins/smtp.py,sha256=yTCUj2XzrJEJ84CIbYcrCUAP6Mg59aZfu6OZDGQyJbQ,7479
|
|
88
88
|
cherrypy_foundation/plugins/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
89
|
cherrypy_foundation/plugins/tests/test_db.py,sha256=PELi5ojj8VqcXyPbpaig2Ac4ZqlR_gEPGUYrWJPANWg,3921
|
|
90
90
|
cherrypy_foundation/plugins/tests/test_ldap.py,sha256=7EhFvhxwDCCBoNlAD5XpZxLtKvkrjpQxrtCHMT9wkQ4,14393
|
|
91
91
|
cherrypy_foundation/plugins/tests/test_scheduler.py,sha256=I-ZuQhMvCCvqFDwukwsyz_UkdJJ8JSLTkAanUo24GCE,3564
|
|
92
|
+
cherrypy_foundation/plugins/tests/test_scheduler_db.py,sha256=-iZnLgb3qsJzs0p7JAw6TQswRtExCmHAtYXqRNPZn7U,3101
|
|
92
93
|
cherrypy_foundation/plugins/tests/test_smtp.py,sha256=qs5yezIpSXkBmLmFlqckfPW7NmntHZxQjDSkdQG_dNE,4183
|
|
93
94
|
cherrypy_foundation/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
95
|
cherrypy_foundation/tests/test_error_page.py,sha256=8yLK8OGbJIdUjilFIHMNBZadLKHrXnD6KSmQ3Da4LaQ,2399
|
|
@@ -103,7 +104,7 @@ cherrypy_foundation/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
103
104
|
cherrypy_foundation/tools/auth.py,sha256=lTSajxCiReMzm-Fl-xhTByi4yFnInEWOoNsmUMnHQhs,9761
|
|
104
105
|
cherrypy_foundation/tools/auth_mfa.py,sha256=VaLvBz9wo6jTx-2mCGqFXPxl-z14f8UMWvd6_xeXd40,9212
|
|
105
106
|
cherrypy_foundation/tools/errors.py,sha256=ELpAj0N9kIxC22QW5xDQJz60zMpCwgm-Twu2WpELM1A,1005
|
|
106
|
-
cherrypy_foundation/tools/i18n.py,sha256=
|
|
107
|
+
cherrypy_foundation/tools/i18n.py,sha256=VQJs7hIv-BM9_FIU6-pwRp65eeroHoQLU7QrV5V_rbI,14641
|
|
107
108
|
cherrypy_foundation/tools/jinja2.py,sha256=nppYnk2ASDsyfNHF9m83W4foiN3MhcwDJvo5baEgnGU,5520
|
|
108
109
|
cherrypy_foundation/tools/ratelimit.py,sha256=pT7vZRmjltNeuiQpdyXOmnpG9BcXjLaj-AXJ0e2x_zw,8300
|
|
109
110
|
cherrypy_foundation/tools/secure_headers.py,sha256=Yh-iA_Js4MUsx5nq4ilbc-iWy90ZC0oMb3TJJD_UwYo,3921
|
|
@@ -111,7 +112,7 @@ cherrypy_foundation/tools/sessions_timeout.py,sha256=6iBWJntPMk_Qt94fBSfBISf1IXI
|
|
|
111
112
|
cherrypy_foundation/tools/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
112
113
|
cherrypy_foundation/tools/tests/test_auth.py,sha256=oeM5t38M8DUC9dYn59dcf00jdGY6Ry0jZWhQd_PYQUk,2847
|
|
113
114
|
cherrypy_foundation/tools/tests/test_auth_mfa.py,sha256=911hnBbdg5CKb613uIBrlggoTAyBU9SoL7Sxd-tIKS0,15008
|
|
114
|
-
cherrypy_foundation/tools/tests/test_i18n.py,sha256=
|
|
115
|
+
cherrypy_foundation/tools/tests/test_i18n.py,sha256=T9j9fF7AwH_H4TJnC4CbAOS1a1D2Z4xPxaOmiP0By34,9091
|
|
115
116
|
cherrypy_foundation/tools/tests/test_jinja2.py,sha256=_dkRJpjB0ybDV6YO0uEFFO8LAcWgVu3VBB8_vWthQ48,4296
|
|
116
117
|
cherrypy_foundation/tools/tests/test_ratelimit.py,sha256=rrqybwMbh1GFlF2-Ut57zHPAc1uqX88aqea6VS_6p5E,3449
|
|
117
118
|
cherrypy_foundation/tools/tests/components/Button.jinja,sha256=uSLp1GpEIgZNXK_GWglu0E_a1c3jHpDLI66MRfMqGhE,95
|
|
@@ -125,8 +126,8 @@ cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po,sha256=6_Sk9I
|
|
|
125
126
|
cherrypy_foundation/tools/tests/templates/test_jinja2.html,sha256=v9AHxksbBvzE7sesPqE61HMhsvU4juXt3E0ZQo-zXVQ,190
|
|
126
127
|
cherrypy_foundation/tools/tests/templates/test_jinja2_i18n.html,sha256=98S51dgG7Vb4rvMZNZvomw1D9pBiM4g6pdlxAgvrxXA,373
|
|
127
128
|
cherrypy_foundation/tools/tests/templates/test_jinjax.html,sha256=NT19UaUzm8FRKOIc6H6HNGPDJU6KATnakd8zf3BCeAs,153
|
|
128
|
-
cherrypy_foundation-1.0.
|
|
129
|
-
cherrypy_foundation-1.0.
|
|
130
|
-
cherrypy_foundation-1.0.
|
|
131
|
-
cherrypy_foundation-1.0.
|
|
132
|
-
cherrypy_foundation-1.0.
|
|
129
|
+
cherrypy_foundation-1.0.0a11.dist-info/licenses/LICENSE.md,sha256=trSLYs5qlaow_bBwsLTRKpmTXsXzFksM_YUCMqrgAJQ,35149
|
|
130
|
+
cherrypy_foundation-1.0.0a11.dist-info/METADATA,sha256=01n5K1GISPi32NOQ8r3x1eV6qNd5x7ZS0tpJON9mUe4,2023
|
|
131
|
+
cherrypy_foundation-1.0.0a11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
132
|
+
cherrypy_foundation-1.0.0a11.dist-info/top_level.txt,sha256=B1vQPTLYhpKJ6W0JkRCWyAf8RPcnwJWdYxixv75-4ew,20
|
|
133
|
+
cherrypy_foundation-1.0.0a11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{cherrypy_foundation-1.0.0a10.dist-info → cherrypy_foundation-1.0.0a11.dist-info}/top_level.txt
RENAMED
|
File without changes
|