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.
- cherrypy_foundation/tests/templates/test_url.html +13 -0
- cherrypy_foundation/tests/test_form.py +0 -3
- cherrypy_foundation/tests/test_url.py +157 -0
- cherrypy_foundation/url.py +25 -20
- {cherrypy_foundation-1.0.0a3.dist-info → cherrypy_foundation-1.0.0a4.dist-info}/METADATA +1 -1
- {cherrypy_foundation-1.0.0a3.dist-info → cherrypy_foundation-1.0.0a4.dist-info}/RECORD +9 -7
- {cherrypy_foundation-1.0.0a3.dist-info → cherrypy_foundation-1.0.0a4.dist-info}/WHEEL +0 -0
- {cherrypy_foundation-1.0.0a3.dist-info → cherrypy_foundation-1.0.0a4.dist-info}/licenses/LICENSE.md +0 -0
- {cherrypy_foundation-1.0.0a3.dist-info → cherrypy_foundation-1.0.0a4.dist-info}/top_level.txt +0 -0
|
@@ -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>
|
|
@@ -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&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&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&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&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&foo=1')
|
cherrypy_foundation/url.py
CHANGED
|
@@ -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,
|
|
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
|
|
39
|
-
|
|
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
|
-
|
|
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
|
|
62
|
-
base = None
|
|
59
|
+
# Outside a request, use cherrypy.tools.proxy config
|
|
63
60
|
if not cherrypy.request.app:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
@@ -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=
|
|
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=
|
|
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.
|
|
122
|
-
cherrypy_foundation-1.0.
|
|
123
|
-
cherrypy_foundation-1.0.
|
|
124
|
-
cherrypy_foundation-1.0.
|
|
125
|
-
cherrypy_foundation-1.0.
|
|
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,,
|
|
File without changes
|
{cherrypy_foundation-1.0.0a3.dist-info → cherrypy_foundation-1.0.0a4.dist-info}/licenses/LICENSE.md
RENAMED
|
File without changes
|
{cherrypy_foundation-1.0.0a3.dist-info → cherrypy_foundation-1.0.0a4.dist-info}/top_level.txt
RENAMED
|
File without changes
|