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.
@@ -16,7 +16,7 @@
16
16
 
17
17
  from collections import namedtuple
18
18
 
19
- import cherrypy
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
- if 'flash' not in cherrypy.session:
31
- cherrypy.session['flash'] = []
32
- # Support Markup and string
33
- if hasattr(message, '__html__'):
34
- flash_message = FlashMessage(message, level)
35
- else:
36
- flash_message = FlashMessage(str(message), level)
37
- cherrypy.session['flash'].append(flash_message)
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
- if 'flash' in cherrypy.session:
45
- messages = cherrypy.session['flash']
46
- del cherrypy.session['flash']
47
- return messages
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 []
@@ -1,20 +1,4 @@
1
- # Session timeout tool for cherrypy
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():
@@ -69,7 +69,7 @@ class LoginForm(CherryForm):
69
69
  )
70
70
 
71
71
 
72
- @cherrypy.tools.sessions(locking='explicit')
72
+ @cherrypy.tools.sessions()
73
73
  @cherrypy.tools.jinja2(env=env)
74
74
  class Root:
75
75
 
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cherrypy-foundation
3
- Version: 1.0.0a19
3
+ Version: 1.0.0a21
4
4
  Summary: Cherrypy-foundation
5
5
  Author-email: Patrik Dufresne <patrik@ikus-soft.com>
6
6
  License: GPLv3
@@ -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=qCghKgVgtjbQVrm6xyIIfRKzf03njFHh6b4tnawzkUM,1542
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=uboQcf4W2zsHFoplRtSziZu1KzvhsudIehHGbG1XYW8,2977
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=wfmKcWTEOPi6BOAUy9oDfkX7LJf9DfxtNRER8R3Ma2g,5560
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.0a19.dist-info/licenses/LICENSE.md,sha256=trSLYs5qlaow_bBwsLTRKpmTXsXzFksM_YUCMqrgAJQ,35149
132
- cherrypy_foundation-1.0.0a19.dist-info/METADATA,sha256=etkiHbuCu6NKyrzniIbKIfhxMKdwIVfr6A3XGHy6yyo,3551
133
- cherrypy_foundation-1.0.0a19.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
134
- cherrypy_foundation-1.0.0a19.dist-info/top_level.txt,sha256=B1vQPTLYhpKJ6W0JkRCWyAf8RPcnwJWdYxixv75-4ew,20
135
- cherrypy_foundation-1.0.0a19.dist-info/RECORD,,
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,,