cherrypy-foundation 1.0.0a19__py3-none-any.whl → 1.0.0a21__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/flash.py +15 -13
- cherrypy_foundation/sessions.py +29 -18
- cherrypy_foundation/tests/test_form.py +1 -1
- cherrypy_foundation/tests/test_sessions.py +89 -0
- {cherrypy_foundation-1.0.0a19.dist-info → cherrypy_foundation-1.0.0a21.dist-info}/METADATA +1 -1
- {cherrypy_foundation-1.0.0a19.dist-info → cherrypy_foundation-1.0.0a21.dist-info}/RECORD +9 -8
- {cherrypy_foundation-1.0.0a19.dist-info → cherrypy_foundation-1.0.0a21.dist-info}/WHEEL +0 -0
- {cherrypy_foundation-1.0.0a19.dist-info → cherrypy_foundation-1.0.0a21.dist-info}/licenses/LICENSE.md +0 -0
- {cherrypy_foundation-1.0.0a19.dist-info → cherrypy_foundation-1.0.0a21.dist-info}/top_level.txt +0 -0
cherrypy_foundation/flash.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
from collections import namedtuple
|
|
18
18
|
|
|
19
|
-
import
|
|
19
|
+
from cherrypy_foundation.sessions import session_lock
|
|
20
20
|
|
|
21
21
|
FlashMessage = namedtuple('FlashMessage', ['message', 'level'])
|
|
22
22
|
|
|
@@ -27,22 +27,24 @@ def flash(message, level='info'):
|
|
|
27
27
|
"""
|
|
28
28
|
assert message
|
|
29
29
|
assert level in ['info', 'error', 'warning', 'success']
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
with session_lock() as session:
|
|
31
|
+
if 'flash' not in session:
|
|
32
|
+
session['flash'] = []
|
|
33
|
+
# Support Markup and string
|
|
34
|
+
if hasattr(message, '__html__'):
|
|
35
|
+
flash_message = FlashMessage(message, level)
|
|
36
|
+
else:
|
|
37
|
+
flash_message = FlashMessage(str(message), level)
|
|
38
|
+
session['flash'].append(flash_message)
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
def get_flashed_messages():
|
|
41
42
|
"""
|
|
42
43
|
Return all flash message.
|
|
43
44
|
"""
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
with session_lock() as session:
|
|
46
|
+
if 'flash' in session:
|
|
47
|
+
messages = session['flash']
|
|
48
|
+
del session['flash']
|
|
49
|
+
return messages
|
|
48
50
|
return []
|
cherrypy_foundation/sessions.py
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Copyright (C) 2012-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
|
-
# BFH Science Self-Service Portal
|
|
1
|
+
# Cherrypy-foundation
|
|
18
2
|
# Copyright (C) 2025-2026 IKUS Software
|
|
19
3
|
#
|
|
20
4
|
# This program is free software: you can redistribute it and/or modify
|
|
@@ -29,6 +13,10 @@
|
|
|
29
13
|
#
|
|
30
14
|
# You should have received a copy of the GNU General Public License
|
|
31
15
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
32
20
|
import time
|
|
33
21
|
from contextlib import contextmanager
|
|
34
22
|
|
|
@@ -38,7 +26,6 @@ from cherrypy.lib import locking
|
|
|
38
26
|
from cherrypy.lib.sessions import FileSession as CPFileSession
|
|
39
27
|
|
|
40
28
|
|
|
41
|
-
# Issue in cherrypy: https://github.com/cherrypy/cherrypy/issues/2065
|
|
42
29
|
class FileSession(CPFileSession):
|
|
43
30
|
"""
|
|
44
31
|
Override implementation of cherrpy file session to improve file locking.
|
|
@@ -46,6 +33,8 @@ class FileSession(CPFileSession):
|
|
|
46
33
|
|
|
47
34
|
def acquire_lock(self, path=None):
|
|
48
35
|
"""Acquire an exclusive lock on the currently-loaded session data."""
|
|
36
|
+
# See Issue https://github.com/cherrypy/cherrypy/issues/2065
|
|
37
|
+
|
|
49
38
|
if path is None:
|
|
50
39
|
path = self._get_file_path()
|
|
51
40
|
path += self.LOCK_SUFFIX
|
|
@@ -62,6 +51,28 @@ class FileSession(CPFileSession):
|
|
|
62
51
|
if self.debug:
|
|
63
52
|
cherrypy.log('Lock acquired.', 'TOOLS.SESSIONS')
|
|
64
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
|
+
|
|
65
76
|
|
|
66
77
|
@contextmanager
|
|
67
78
|
def session_lock():
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
|
|
17
|
+
import os
|
|
18
|
+
import tempfile
|
|
19
|
+
|
|
20
|
+
import cherrypy
|
|
21
|
+
from cherrypy.test import helper
|
|
22
|
+
|
|
23
|
+
from cherrypy_foundation.sessions import FileSession, session_lock
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@cherrypy.tools.sessions(on=True, locking='explicit', storage_class=FileSession)
|
|
27
|
+
class Root:
|
|
28
|
+
|
|
29
|
+
@cherrypy.expose
|
|
30
|
+
def index(self, value='OK'):
|
|
31
|
+
if value:
|
|
32
|
+
with session_lock() as s:
|
|
33
|
+
s['value'] = value
|
|
34
|
+
return s['value']
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FileSessionTest(helper.CPWebCase):
|
|
38
|
+
interactive = False
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def setup_class(cls):
|
|
42
|
+
cls.tempdir = tempfile.TemporaryDirectory(prefix='cherrypy-foundation-', suffix='-file-session-test')
|
|
43
|
+
cls.storage_path = cls.tempdir.name
|
|
44
|
+
super().setup_class()
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def teardown_class(cls):
|
|
48
|
+
cls.tempdir.cleanup()
|
|
49
|
+
super().teardown_class()
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def setup_server(cls):
|
|
53
|
+
cherrypy.config.update(
|
|
54
|
+
{
|
|
55
|
+
'tools.sessions.storage_path': cls.storage_path,
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
cherrypy.tree.mount(Root(), '/')
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def _session_id(self):
|
|
62
|
+
"""Return session id from cookie."""
|
|
63
|
+
if hasattr(self, 'cookies') and self.cookies:
|
|
64
|
+
for unused, value in self.cookies:
|
|
65
|
+
for part in value.split(';'):
|
|
66
|
+
key, unused, value = part.partition('=')
|
|
67
|
+
if key == 'session_id':
|
|
68
|
+
return value
|
|
69
|
+
|
|
70
|
+
def test_get_page(self):
|
|
71
|
+
# Given a page with session enabled
|
|
72
|
+
# When the page get queried
|
|
73
|
+
self.getPage("/")
|
|
74
|
+
# Then a session is created with a id
|
|
75
|
+
self.assertStatus(200)
|
|
76
|
+
self.assertTrue(self._session_id)
|
|
77
|
+
# Then this session is created on disk.
|
|
78
|
+
s = FileSession(id=self._session_id, storage_path=self.storage_path)
|
|
79
|
+
self.assertTrue(s._exists())
|
|
80
|
+
# When session timeout and get clean-up
|
|
81
|
+
s.acquire_lock()
|
|
82
|
+
s.load()
|
|
83
|
+
s.timeout = 0
|
|
84
|
+
s.save()
|
|
85
|
+
s.clean_up()
|
|
86
|
+
# Then session get deleted
|
|
87
|
+
self.assertFalse(s._exists())
|
|
88
|
+
# Lock file also get deleted.
|
|
89
|
+
self.assertFalse(os.path.exists(s._get_file_path() + FileSession.LOCK_SUFFIX))
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
cherrypy_foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
cherrypy_foundation/error_page.py,sha256=GEg_KZGRE63ZsTTtmrEpE9GsYcm6shZvp5KLR53sIMA,3248
|
|
3
|
-
cherrypy_foundation/flash.py,sha256=
|
|
3
|
+
cherrypy_foundation/flash.py,sha256=vraKvmYmjdV1cGsPSxTLVsekwqOnoyiVPqKjv6UW5OU,1646
|
|
4
4
|
cherrypy_foundation/form.py,sha256=8zp72fpYrmMGsIK5LxVY7sL_qqhoMDnIQoMND1s6CXk,3994
|
|
5
5
|
cherrypy_foundation/logging.py,sha256=JyzcuAu3HWzvMTSFBuKpwgnJLz1lRlx-j6EkuTwlb6I,3463
|
|
6
6
|
cherrypy_foundation/passwd.py,sha256=iyz3SgcvpgxsZgPUoTEXiqdKtwOANPrRKYhVPm2d_NQ,2139
|
|
7
|
-
cherrypy_foundation/sessions.py,sha256=
|
|
7
|
+
cherrypy_foundation/sessions.py,sha256=6OmjrJtX2Hhjhx5Q8Cy1nwG6_Ur6PwPErfPPA7FfzYg,3180
|
|
8
8
|
cherrypy_foundation/url.py,sha256=WtfD0XEOs5FNmLvxhnBvi9aW4hQpUz0ZZXPfEXd4To8,2849
|
|
9
9
|
cherrypy_foundation/widgets.py,sha256=-wxNv3wIgf7eB8WyyycfsqSCXep3YhZgBK_5PJ1qYbY,1549
|
|
10
10
|
cherrypy_foundation/components/ColorModes.jinja,sha256=Ai2fy1qHFwEgutvyvvGjKJmffcBdNb7wmY20DJqZ8R4,3528
|
|
@@ -97,9 +97,10 @@ cherrypy_foundation/plugins/tests/test_smtp.py,sha256=LYUXjDe8TtyruBSm18N73nSxHi
|
|
|
97
97
|
cherrypy_foundation/tests/__init__.py,sha256=bbdyalzM2mwBbId-7JBPXYuRzk7oUBCncq33ypD9v3Y,2818
|
|
98
98
|
cherrypy_foundation/tests/test_error_page.py,sha256=EmsCXYkFFMReNSORZYCo7qKvL2x98QgtKIlv1nFKIUU,2399
|
|
99
99
|
cherrypy_foundation/tests/test_flash.py,sha256=NNbT3XI1dHk0hXqJsWndCmPPohK9ig87ZYJaRNPJxR8,1994
|
|
100
|
-
cherrypy_foundation/tests/test_form.py,sha256
|
|
100
|
+
cherrypy_foundation/tests/test_form.py,sha256=-2h38e00hWlYNJeVWrUB2V11UZ3x0FmTcsCez5vWoas,5542
|
|
101
101
|
cherrypy_foundation/tests/test_logging.py,sha256=Lg1yGwFHTSpGbPvNXBi9risypZziJlAOMi1DeennxRs,2623
|
|
102
102
|
cherrypy_foundation/tests/test_passwd.py,sha256=nGqJZYwRPziifLvf5rm3YifxAvIOFdU_6LkHaUvRk8w,1951
|
|
103
|
+
cherrypy_foundation/tests/test_sessions.py,sha256=_jcCn75hMGr7ETucWZnCqWcI3pw5PCdlzPQuxdE4yZE,2873
|
|
103
104
|
cherrypy_foundation/tests/test_url.py,sha256=sSNwK7KZJcrLtnf_XROYQAPQoYNO5lAsa2BjhFjgmws,6434
|
|
104
105
|
cherrypy_foundation/tests/templates/test_flash.html,sha256=b1S4I9v0n-Y1yoTUh2ZKNysR1NMrqv8ldvqONtmInzw,213
|
|
105
106
|
cherrypy_foundation/tests/templates/test_form.html,sha256=liubTm2q74-3hqQb4weaGJU3sq4vdq868GdVahBafSQ,333
|
|
@@ -128,8 +129,8 @@ cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po,sha256=6_Sk9I
|
|
|
128
129
|
cherrypy_foundation/tools/tests/templates/test_jinja2.html,sha256=s1bHmy-lyf0YW0t-LOx3ugILV2kqFoBNYxziWgrZbo0,216
|
|
129
130
|
cherrypy_foundation/tools/tests/templates/test_jinjax.html,sha256=NImzIW0mUHxilFd61PSoxFC-yu1nayEVwv-5zlgD9yo,179
|
|
130
131
|
cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html,sha256=yre8j7HBjpTQZHpM0PuB3ASGD3O4vKkJ-y72Fm6STgY,771
|
|
131
|
-
cherrypy_foundation-1.0.
|
|
132
|
-
cherrypy_foundation-1.0.
|
|
133
|
-
cherrypy_foundation-1.0.
|
|
134
|
-
cherrypy_foundation-1.0.
|
|
135
|
-
cherrypy_foundation-1.0.
|
|
132
|
+
cherrypy_foundation-1.0.0a21.dist-info/licenses/LICENSE.md,sha256=trSLYs5qlaow_bBwsLTRKpmTXsXzFksM_YUCMqrgAJQ,35149
|
|
133
|
+
cherrypy_foundation-1.0.0a21.dist-info/METADATA,sha256=iAkKOmpDqv05F3r7I44szCpJbFck3F6ik0f1qvvUqek,3551
|
|
134
|
+
cherrypy_foundation-1.0.0a21.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
135
|
+
cherrypy_foundation-1.0.0a21.dist-info/top_level.txt,sha256=B1vQPTLYhpKJ6W0JkRCWyAf8RPcnwJWdYxixv75-4ew,20
|
|
136
|
+
cherrypy_foundation-1.0.0a21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{cherrypy_foundation-1.0.0a19.dist-info → cherrypy_foundation-1.0.0a21.dist-info}/top_level.txt
RENAMED
|
File without changes
|