cherrypy-foundation 1.0.0a3__py3-none-any.whl → 1.0.0a4__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.
@@ -0,0 +1,13 @@
1
+ <html>
2
+ <head>
3
+ <title>test-url</title>
4
+ </head>
5
+ <body>
6
+ Empty: {{ url_for(".", **kwargs) }}<br/>
7
+ Dot: {{ url_for(".", **kwargs) }}<br/>
8
+ Slash: {{ url_for("/", **kwargs) }}<br/>
9
+ Page: {{ url_for("my-page", **kwargs) }}<br/>
10
+ Absolute page: {{ url_for("/my-page", **kwargs) }}<br/>
11
+ Query: {{ url_for("my-page", foo='1', bar='test with space', **kwargs) }}<br/>
12
+ </body>
13
+ </html>
@@ -29,9 +29,6 @@ HAS_JINJAX = importlib.util.find_spec("jinjax") is not None
29
29
 
30
30
  env = cherrypy.tools.jinja2.create_env(
31
31
  package_name=__package__,
32
- globals={
33
- 'const1': 'STATIC VALUE',
34
- },
35
32
  )
36
33
 
37
34
 
@@ -0,0 +1,157 @@
1
+ # CherryPy Foundation
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
+
17
+ import cherrypy
18
+ from cherrypy.test import helper
19
+
20
+ import cherrypy_foundation.tools.jinja2 # noqa
21
+
22
+ from ..url import url_for
23
+
24
+ env = cherrypy.tools.jinja2.create_env(
25
+ package_name=__package__,
26
+ globals={
27
+ 'url_for': url_for,
28
+ },
29
+ )
30
+
31
+
32
+ class SubPage:
33
+
34
+ @cherrypy.expose
35
+ @cherrypy.tools.jinja2(template='test_url.html')
36
+ def index(self, **kwargs):
37
+ return {'kwargs': kwargs}
38
+
39
+
40
+ @cherrypy.tools.proxy(base='https://www.example.com')
41
+ class ProxiedPage:
42
+
43
+ @cherrypy.expose
44
+ @cherrypy.tools.jinja2(template='test_url.html')
45
+ def index(self, **kwargs):
46
+ return {'kwargs': kwargs}
47
+
48
+
49
+ @cherrypy.tools.jinja2(env=env)
50
+ class Root:
51
+ sub_page = SubPage()
52
+ proxied = ProxiedPage()
53
+
54
+ @cherrypy.expose
55
+ @cherrypy.tools.jinja2(template='test_url.html')
56
+ def index(self, **kwargs):
57
+ return {'kwargs': kwargs}
58
+
59
+
60
+ class UrlTest(helper.CPWebCase):
61
+ default_lang = None
62
+ interactive = False
63
+
64
+ @classmethod
65
+ def setup_server(cls):
66
+ cherrypy.tree.mount(Root(), '/')
67
+
68
+ def test_url_for(self):
69
+ self.assertEqual(url_for("foo", "bar"), 'http://127.0.0.1:54583/foo/bar')
70
+ self.assertEqual(url_for("foo", "bar", _relative='server'), '/foo/bar')
71
+ # Outside a request, relative url doesn't make alot of sens.
72
+ self.assertEqual(url_for("foo", "bar", _relative=1), '127.0.0.1:54583/foo/bar')
73
+ self.assertEqual(url_for("foo", "bar", _base='http://test.com'), 'http://test.com/foo/bar')
74
+ self.assertEqual(
75
+ url_for("mailto:myuser@test.com", subject='Enter you subject', _base=''),
76
+ 'mailto:myuser@test.com?subject=Enter%20you%20subject',
77
+ )
78
+
79
+ def test_get_page(self):
80
+ # Given a form
81
+ # When querying the page that include this form
82
+ self.getPage("/")
83
+ self.assertStatus(200)
84
+ # Then each field is render properly.
85
+ # 1. Check title
86
+ self.assertInBody('test-url')
87
+ # 2. Check user field
88
+ self.assertInBody(f'Empty: http://{self.HOST}:{self.PORT}/')
89
+ self.assertInBody(f'Dot: http://{self.HOST}:{self.PORT}/')
90
+ self.assertInBody(f'Slash: http://{self.HOST}:{self.PORT}/')
91
+ self.assertInBody(f'Page: http://{self.HOST}:{self.PORT}/my-page')
92
+ self.assertInBody(f'Absolute page: http://{self.HOST}:{self.PORT}/my-page')
93
+ self.assertInBody(f'Query: http://{self.HOST}:{self.PORT}/my-page?bar=test+with+space&amp;foo=1')
94
+
95
+ def test_get_page_relative_true(self):
96
+ # Given a form
97
+ # When querying the page that include this form
98
+ self.getPage("/?_relative=1")
99
+ self.assertStatus(200)
100
+ # Then each field is render properly.
101
+ # 1. Check title
102
+ self.assertInBody('test-url')
103
+ # 2. Check user field
104
+ self.assertInBody('Empty: <br/>')
105
+ self.assertInBody('Dot: <br/>')
106
+ self.assertInBody('Slash: <br/>')
107
+ self.assertInBody('Page: my-page<br/>')
108
+ self.assertInBody('Absolute page: my-page<br/>')
109
+ self.assertInBody('Query: my-page?bar=test+with+space&amp;foo=1<br/>')
110
+
111
+ def test_get_page_relative_server(self):
112
+ # Given a form
113
+ # When querying the page that include this form
114
+ self.getPage("/?_relative=server")
115
+ self.assertStatus(200)
116
+ # Then each field is render properly.
117
+ # 1. Check title
118
+ self.assertInBody('test-url')
119
+ # 2. Check user field
120
+ self.assertInBody('Empty: /<br/>')
121
+ self.assertInBody('Dot: /<br/>')
122
+ self.assertInBody('Slash: /<br/>')
123
+ self.assertInBody('Page: /my-page<br/>')
124
+ self.assertInBody('Absolute page: /my-page<br/>')
125
+ self.assertInBody('Query: /my-page?bar=test+with+space&amp;foo=1<br/>')
126
+
127
+ def test_get_page_proxied(self):
128
+ # Given a form
129
+ # When querying the page that include this form
130
+ self.getPage("/proxied/")
131
+ self.assertStatus(200)
132
+ # Then each field is render properly.
133
+ # 1. Check title
134
+ self.assertInBody('test-url')
135
+ # 2. Check user field
136
+ self.assertInBody('Empty: https://www.example.com/proxied/')
137
+ self.assertInBody('Dot: https://www.example.com/proxied/')
138
+ self.assertInBody('Slash: https://www.example.com/')
139
+ self.assertInBody('Page: https://www.example.com/proxied/my-page')
140
+ self.assertInBody('Absolute page: https://www.example.com/my-page')
141
+ self.assertInBody('Query: https://www.example.com/proxied/my-page?bar=test+with+space&amp;foo=1')
142
+
143
+ def test_get_sub_page(self):
144
+ # Given a form
145
+ # When querying the page that include this form
146
+ self.getPage("/sub-page/")
147
+ self.assertStatus(200)
148
+ # Then each field is render properly.
149
+ # 1. Check title
150
+ self.assertInBody('test-url')
151
+ # 2. Check user field
152
+ self.assertInBody(f'Empty: http://{self.HOST}:{self.PORT}/sub-page/')
153
+ self.assertInBody(f'Dot: http://{self.HOST}:{self.PORT}/sub-page/')
154
+ self.assertInBody(f'Slash: http://{self.HOST}:{self.PORT}/')
155
+ self.assertInBody(f'Page: http://{self.HOST}:{self.PORT}/sub-page/my-page')
156
+ self.assertInBody(f'Absolute page: http://{self.HOST}:{self.PORT}/my-page')
157
+ self.assertInBody(f'Query: http://{self.HOST}:{self.PORT}/sub-page/my-page?bar=test+with+space&amp;foo=1')
@@ -14,10 +14,12 @@
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 quote, urlencode, urljoin
18
+
17
19
  import cherrypy
18
20
 
19
21
 
20
- def url_for(*args, relative=None, **kwargs):
22
+ def url_for(*args, _relative=None, _base=None, **kwargs):
21
23
  """
22
24
  Generate a URL for the given endpoint/path (*args) with query params (**kwargs).
23
25
 
@@ -33,34 +35,37 @@ def url_for(*args, relative=None, **kwargs):
33
35
  - Integers are appended as path segments.
34
36
  - When path == "", existing request query parameters are merged (kwargs win).
35
37
  """
36
- path = ""
38
+ path = []
37
39
  for chunk in args:
38
- if isinstance(chunk, str):
39
- if not chunk.startswith('.'):
40
- path += "/"
41
- path += chunk.rstrip("/")
42
- elif isinstance(chunk, int):
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__())
40
+ if hasattr(chunk, '__url_for__') and callable(chunk.__url_for__):
41
+ path.append(str(chunk.__url_for__()))
48
42
  elif hasattr(chunk, 'url_for'):
49
- path += "/"
50
- path += str(chunk.url_for)
43
+ path.append(str(chunk.url_for))
44
+ elif isinstance(chunk, str):
45
+ path.append(chunk)
46
+ elif isinstance(chunk, int):
47
+ path.append(str(chunk))
51
48
  else:
52
49
  raise ValueError('invalid positional arguments, url_for accept str, bytes, int: %r' % chunk)
50
+ path = '/'.join(path)
53
51
  # When path is empty, we are browsing the same page.
54
52
  # Let keep the original query_string to avoid loosing it.
55
- if path == "":
53
+ if not path:
56
54
  params = cherrypy.request.params.copy()
57
55
  params.update(kwargs)
58
56
  qs = [(k, v) for k, v in sorted(params.items()) if v is not None]
59
57
  else:
60
58
  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
59
+ # Outside a request, use cherrypy.tools.proxy config
63
60
  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)
61
+ path = urljoin('/', path)
62
+ if _base is None:
63
+ _base = cherrypy.config.get('tools.proxy.base', None)
64
+ # Use cherrypy to build the URL
65
+ url = cherrypy.url(path=path, relative=_relative, base=_base)
66
+ # Append the query string
67
+ if qs and url.startswith('mailto:'):
68
+ return url + '?' + urlencode(qs, quote_via=quote)
69
+ elif qs:
70
+ return url + '?' + urlencode(qs)
71
+ return url
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cherrypy-foundation
3
- Version: 1.0.0a3
3
+ Version: 1.0.0a4
4
4
  Summary: CherryPy Foundation
5
5
  Author-email: Patrik Dufresne <patrik@ikus-soft.com>
6
6
  License: GPLv3
@@ -4,7 +4,7 @@ cherrypy_foundation/flash.py,sha256=fFRbutUX6c1lVHqjehmO9y98dJgmfNCjhd76t2mth2s,
4
4
  cherrypy_foundation/form.py,sha256=8c9dO0o47sK3CBosTkGXoVRtzNQwY0aw0vNZfTqmhvo,3994
5
5
  cherrypy_foundation/logging.py,sha256=YIOK5ZAZLCv52YDdP66yBYpEX1C336JnI3wnrTKl1Lw,3468
6
6
  cherrypy_foundation/passwd.py,sha256=ZGdrBNKtLP75l01W6VEd8cIjSQ3guJ_YVPEfbSew7T0,2144
7
- cherrypy_foundation/url.py,sha256=n12rU2R3VoPbGJ01-iXIMlZL4ohpVhk3vOc-__KkPzE,2814
7
+ cherrypy_foundation/url.py,sha256=sC3DOZApIcapiIiqcUZ83LI1UFyc_f06TER-qJP-xCs,3026
8
8
  cherrypy_foundation/widgets.py,sha256=0B5Y2V6x5Ufl6ExR3tc0Olrzj7N4TAAOtqGq_MUxBG0,1549
9
9
  cherrypy_foundation/components/ColorModes.jinja,sha256=8MzkeZsra1wtIdiaQKc7UQUbMfRMUlmM6e9X7V1vfq0,3501
10
10
  cherrypy_foundation/components/Datatable.css,sha256=7wSwgdA61vYCdEuQ0bp2o0oSvu5mGLN1c6ovCUSe718,947
@@ -91,9 +91,11 @@ cherrypy_foundation/plugins/tests/test_scheduler.py,sha256=I-ZuQhMvCCvqFDwukwsyz
91
91
  cherrypy_foundation/plugins/tests/test_smtp.py,sha256=qs5yezIpSXkBmLmFlqckfPW7NmntHZxQjDSkdQG_dNE,4183
92
92
  cherrypy_foundation/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
93
  cherrypy_foundation/tests/test_error_page.py,sha256=8u5p6lv_I4XvfGipjJXNFwW7G1N8AK8IOnc_Th-Ebto,2181
94
- cherrypy_foundation/tests/test_form.py,sha256=urYi0qC8JqelopdeLBHvtndEv0QJ0hwlv3JkpAvdCYQ,4324
94
+ cherrypy_foundation/tests/test_form.py,sha256=KvVA7OEqaDR-A-1EJdA-WlXkqyK9YJkRHhoUvGx1zr8,4269
95
95
  cherrypy_foundation/tests/test_passwd.py,sha256=gC5O4yhHyU1YRYuDc0pG0T_5zvrG2qrr6P822iyK3Rg,1956
96
+ cherrypy_foundation/tests/test_url.py,sha256=bjq9mCyuYpsykPYLGmgOjvP4RlFywIsRdTNslndcQoE,6025
96
97
  cherrypy_foundation/tests/templates/test_form.html,sha256=sm-n2cYvih2vbDE4Y8kkERSoulnKAbwoefbzBggMMnA,189
98
+ cherrypy_foundation/tests/templates/test_url.html,sha256=nbDwrB9F-dVyn3cqcV2-5-ZOqQH55lgUi1cpVuAJh_o,443
97
99
  cherrypy_foundation/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
100
  cherrypy_foundation/tools/auth.py,sha256=lTSajxCiReMzm-Fl-xhTByi4yFnInEWOoNsmUMnHQhs,9761
99
101
  cherrypy_foundation/tools/auth_mfa.py,sha256=VaLvBz9wo6jTx-2mCGqFXPxl-z14f8UMWvd6_xeXd40,9212
@@ -118,8 +120,8 @@ cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po,sha256=6_Sk9I
118
120
  cherrypy_foundation/tools/tests/templates/test_jinja2.html,sha256=v9AHxksbBvzE7sesPqE61HMhsvU4juXt3E0ZQo-zXVQ,190
119
121
  cherrypy_foundation/tools/tests/templates/test_jinja2_i18n.html,sha256=98S51dgG7Vb4rvMZNZvomw1D9pBiM4g6pdlxAgvrxXA,373
120
122
  cherrypy_foundation/tools/tests/templates/test_jinjax.html,sha256=NT19UaUzm8FRKOIc6H6HNGPDJU6KATnakd8zf3BCeAs,153
121
- cherrypy_foundation-1.0.0a3.dist-info/licenses/LICENSE.md,sha256=trSLYs5qlaow_bBwsLTRKpmTXsXzFksM_YUCMqrgAJQ,35149
122
- cherrypy_foundation-1.0.0a3.dist-info/METADATA,sha256=rnu-JnpVHWQcZ5ykeEYGGYYQB1eOeg5Uv9YfMou5yvA,2022
123
- cherrypy_foundation-1.0.0a3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
124
- cherrypy_foundation-1.0.0a3.dist-info/top_level.txt,sha256=B1vQPTLYhpKJ6W0JkRCWyAf8RPcnwJWdYxixv75-4ew,20
125
- cherrypy_foundation-1.0.0a3.dist-info/RECORD,,
123
+ cherrypy_foundation-1.0.0a4.dist-info/licenses/LICENSE.md,sha256=trSLYs5qlaow_bBwsLTRKpmTXsXzFksM_YUCMqrgAJQ,35149
124
+ cherrypy_foundation-1.0.0a4.dist-info/METADATA,sha256=_72IwEjxgvFKdSmIFT0-NZRQrHqzL4MwbGTCSF9ZiX8,2022
125
+ cherrypy_foundation-1.0.0a4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
126
+ cherrypy_foundation-1.0.0a4.dist-info/top_level.txt,sha256=B1vQPTLYhpKJ6W0JkRCWyAf8RPcnwJWdYxixv75-4ew,20
127
+ cherrypy_foundation-1.0.0a4.dist-info/RECORD,,