pyweber 1.2.0.dev20260426__tar.gz → 1.2.0.dev20260428__tar.gz
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.
- {pyweber-1.2.0.dev20260426/pyweber.egg-info → pyweber-1.2.0.dev20260428}/PKG-INFO +1 -1
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyproject.toml +1 -1
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/request.py +36 -33
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/response.py +1 -1
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/routes.py +15 -5
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/run.py +7 -7
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/pyweber/pyweber.py +15 -6
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/js.js +8 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428/pyweber.egg-info}/PKG-INFO +1 -1
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/LICENSE +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/MANIFEST.in +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/README.md +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/admin/index.html +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/admin/src/script.js +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/admin/src/style.css +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/cli/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/cli/commands.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/components/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/components/form.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/components/general.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/components/input.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/config/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/config/config.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/connection/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/connection/http.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/connection/reload.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/connection/selector.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/connection/session.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/connection/websocket.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/core/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/core/element.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/core/events.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/core/template.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/core/window.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/cookies.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/create_app.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/element.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/error_pages.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/field.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/field_storage.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/file.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/file_stream.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/headers.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/middleware.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/openapi.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/strem_stats.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/task_manager.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/template_diff.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/models/ws_message.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/pyweber/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/.gitignore +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/config.toml +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/css.css +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/docs.html +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/android-chrome-192x192.png +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/android-chrome-512x512.png +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/apple-touch-icon.png +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/favicon-16x16.png +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/favicon-32x32.png +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/favicon.ico +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/pyweber.png +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/site.webmanifest +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/handlers.js +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/html.html +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/html401.html +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/html404.html +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/html500.html +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/loading.html +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/main.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/pyweber.css +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/update.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/utils/__init__.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/utils/exceptions.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/utils/loads.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/utils/types.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/utils/utils.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber.egg-info/SOURCES.txt +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber.egg-info/dependency_links.txt +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber.egg-info/entry_points.txt +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber.egg-info/requires.txt +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber.egg-info/top_level.txt +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/setup.cfg +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/tests/test_config.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/tests/test_cookies.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/tests/test_response.py +0 -0
- {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/tests/test_template_diff.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyweber"
|
|
7
|
-
version = "1.2.0.
|
|
7
|
+
version = "1.2.0.dev20260428"
|
|
8
8
|
description = "A lightweight Python framework for building and managing web applications."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "DevPythonMZ", email = "pypi.dev@gmail.com" }]
|
|
@@ -42,24 +42,24 @@ class Request: # pragma: no cover
|
|
|
42
42
|
self.__raw_request_asgi = headers
|
|
43
43
|
self.__raw_body = body or b''
|
|
44
44
|
self.__raw_headers: list[tuple[bytes, bytes]] = headers.get('headers', [])
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
else:
|
|
47
47
|
raise TypeError('Request type does not valid')
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
self.client_info = client_info
|
|
50
50
|
self.__additions_headers()
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
@property
|
|
53
53
|
def request_mode(self):
|
|
54
54
|
return self.__request_mode
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
@request_mode.setter
|
|
57
57
|
def request_mode(self, value):
|
|
58
58
|
if not isinstance(value, RequestMode):
|
|
59
59
|
raise TypeError('Request mode does not valid')
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
self.__request_mode = value
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
@property
|
|
64
64
|
def client_info(self): return self.__client_info
|
|
65
65
|
|
|
@@ -67,66 +67,66 @@ class Request: # pragma: no cover
|
|
|
67
67
|
def client_info(self, value: ClientInfo):
|
|
68
68
|
if value and not isinstance(value, ClientInfo):
|
|
69
69
|
raise TypeError('client_info must be a ClientInfo instances')
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
self.__client_info = value or ClientInfo(host=None, port=0)
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
@property
|
|
74
74
|
def raw_headers(self):
|
|
75
75
|
if self.request_mode.value == 'asgi':
|
|
76
76
|
return self.__raw_request_asgi
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
return self.__raw_request_wsgi
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
@property
|
|
81
81
|
def raw_body(self):
|
|
82
82
|
return self.__raw_body
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
@property
|
|
85
85
|
def host(self):
|
|
86
86
|
return self.headers.get('host')
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
@property
|
|
89
89
|
def port(self):
|
|
90
90
|
try:
|
|
91
91
|
return int(self.headers.get('host', '0').split(':')[-1])
|
|
92
92
|
except:
|
|
93
93
|
return 0
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
@property
|
|
96
96
|
def content_length(self):
|
|
97
97
|
return int(self.headers.get('content-length'))
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
@property
|
|
100
100
|
def content_type(self):
|
|
101
101
|
return self.headers.get('content-type', '')
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
@property
|
|
104
104
|
def user_agent(self):
|
|
105
105
|
return self.headers.get('user-agent')
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
@property
|
|
108
108
|
def origin(self):
|
|
109
109
|
return self.headers.get('origin')
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
@property
|
|
112
112
|
def referrer(self):
|
|
113
113
|
return self.headers.get('referrer')
|
|
114
|
-
|
|
114
|
+
|
|
115
115
|
@property
|
|
116
116
|
def accept(self):
|
|
117
117
|
return [val.strip().split(';') for val in self.headers.get('accept', '').split(',') if val]
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
@property
|
|
120
120
|
def accept_encoding(self):
|
|
121
121
|
return [val.strip().split(';') for val in self.headers.get('accept-encoding', '').split(',') if val]
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
@property
|
|
124
124
|
def accept_language(self):
|
|
125
125
|
return [val.strip().split(';') for val in self.headers.get('accept-language', '').split(',') if val]
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
@property
|
|
128
128
|
def cookies(self):
|
|
129
|
-
return {cookie.split('=')[0]: cookie.split('=')[-1] for cookie in self.headers.get('cookie', '').split(';') if cookie}
|
|
129
|
+
return {cookie.split('=')[0].strip(): cookie.split('=')[-1].strip() for cookie in self.headers.get('cookie', '').split(';') if cookie}
|
|
130
130
|
|
|
131
131
|
@property
|
|
132
132
|
def accept_control_request_headers(self):
|
|
@@ -136,9 +136,9 @@ class Request: # pragma: no cover
|
|
|
136
136
|
def headers(self):
|
|
137
137
|
if self.request_mode.value == 'asgi':
|
|
138
138
|
return {header[0].decode(): header[1].decode() for header in self.__raw_headers}
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
return self.__parse_headers_wsgi()
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
@property
|
|
143
143
|
def body(self) -> Union[dict[str, Union[list[File], str]]]:
|
|
144
144
|
if self.content_type == ContentTypes.json.value:
|
|
@@ -148,15 +148,18 @@ class Request: # pragma: no cover
|
|
|
148
148
|
elif ContentTypes.form_data.value in self.content_type:
|
|
149
149
|
return self.__parse_form_data()
|
|
150
150
|
return {'body': self.__raw_body}
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
@property
|
|
153
153
|
def first_line(self):
|
|
154
154
|
if self.request_mode.value == 'wsgi':
|
|
155
155
|
return self.__raw_headers.split(self.__line_splitter, 1)[0].strip()
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
full_path = f"{self.path}?{'&'.join(['{key}={value}'.format(key=key, value=value) for key, value in self.query_params.items()])}" if self.query_params else self.path
|
|
158
158
|
return f"{self.method} {full_path} {self.scheme}"
|
|
159
159
|
|
|
160
|
+
@property
|
|
161
|
+
def full_path(self): return self.first_line.split(' ', 2)[1].strip()
|
|
162
|
+
|
|
160
163
|
def __parse_form_data(self):
|
|
161
164
|
fs = FieldStorage(self.content_type, callbacks=self.__raw_body)
|
|
162
165
|
body: dict[str, list[File] | str] = {}
|
|
@@ -174,10 +177,10 @@ class Request: # pragma: no cover
|
|
|
174
177
|
|
|
175
178
|
body[field.name].append(field.value)
|
|
176
179
|
else:
|
|
177
|
-
body[field.name] = field.value
|
|
178
|
-
|
|
180
|
+
body[field.name] = field.value
|
|
181
|
+
|
|
179
182
|
return body
|
|
180
|
-
|
|
183
|
+
|
|
181
184
|
def __additions_headers(self):
|
|
182
185
|
if self.request_mode.value == 'asgi':
|
|
183
186
|
self.method: str = self.raw_headers.get('method')
|
|
@@ -193,17 +196,17 @@ class Request: # pragma: no cover
|
|
|
193
196
|
self.query_params = {
|
|
194
197
|
key: ';'.join(val) for key, val in parse_qs(line_info[1].split('?', 1)[-1]).items() if val
|
|
195
198
|
} if len(line_info) >= 2 else {}
|
|
196
|
-
|
|
199
|
+
|
|
197
200
|
def __parse_headers_wsgi(self) -> dict[str, str]:
|
|
198
201
|
return {header.split(':', 1)[0].strip().lower(): header.split(':', 1)[-1].strip() for header in self.__raw_headers.split(self.__line_splitter)[1::]}
|
|
199
|
-
|
|
202
|
+
|
|
200
203
|
@property
|
|
201
204
|
def __line_splitter(self):
|
|
202
205
|
return '\r\n'
|
|
203
|
-
|
|
206
|
+
|
|
204
207
|
@property
|
|
205
208
|
def request_parts_splitter(self):
|
|
206
209
|
return '\r\n\r\n'
|
|
207
210
|
|
|
208
211
|
def __repr__(self):
|
|
209
|
-
return f"Request(method={self.method}, mode={self.request_mode})"
|
|
212
|
+
return f"Request(method={self.method}, mode={self.request_mode})"
|
|
@@ -30,7 +30,7 @@ class Response:
|
|
|
30
30
|
"Server": 'Pyweber/1.0',
|
|
31
31
|
"Date": datetime.now(timezone.utc).strftime("%a, %d %b %Y %H:%M:%S GMT"),
|
|
32
32
|
"Set-Cookie": cookies,
|
|
33
|
-
"Request-Path": request.
|
|
33
|
+
"Request-Path": request.full_path,
|
|
34
34
|
"Response-Path": route,
|
|
35
35
|
"Access-Control-Allow-Origin": request.origin,
|
|
36
36
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
@@ -544,7 +544,6 @@ class RouteManager: # pragma: no cover
|
|
|
544
544
|
|
|
545
545
|
def get_route_by_path(self, route: str, follow_redirect: bool = True):
|
|
546
546
|
path, _ = self.resolve_path(route=route)
|
|
547
|
-
path = path.split('?', 1)[0]
|
|
548
547
|
|
|
549
548
|
if follow_redirect in [True, 1] and self.is_redirected(route=path):
|
|
550
549
|
redirect_route = self.get_redirected_route(route=path)
|
|
@@ -597,14 +596,25 @@ class RouteManager: # pragma: no cover
|
|
|
597
596
|
def __resolve_path__(route: str, list_routes: dict[str, Route | RedirectRoute]):
|
|
598
597
|
kwargs: dict[str, str] = {}
|
|
599
598
|
|
|
599
|
+
# Separa path dos query params antes de qualquer processamento
|
|
600
|
+
clean_route, _, query_string = route.partition('?')
|
|
601
|
+
|
|
602
|
+
# Parse dos query params
|
|
603
|
+
query_params: dict[str, str] = {}
|
|
604
|
+
if query_string:
|
|
605
|
+
for pair in query_string.split('&'):
|
|
606
|
+
key, _, val = pair.partition('=')
|
|
607
|
+
if key:
|
|
608
|
+
query_params[key] = val
|
|
609
|
+
|
|
600
610
|
for path in list_routes:
|
|
601
611
|
l_route = path.strip('/').split('/')
|
|
602
|
-
r_route =
|
|
612
|
+
r_route = clean_route.strip('/').split('/') # usa o path limpo
|
|
603
613
|
|
|
604
614
|
if len(l_route) != len(r_route):
|
|
605
615
|
continue
|
|
606
616
|
|
|
607
|
-
if '{' in path and len(
|
|
617
|
+
if '{' in path and len(clean_route) == 1:
|
|
608
618
|
continue
|
|
609
619
|
|
|
610
620
|
match = True
|
|
@@ -619,9 +629,9 @@ class RouteManager: # pragma: no cover
|
|
|
619
629
|
break
|
|
620
630
|
|
|
621
631
|
if match:
|
|
622
|
-
return path, kwargs
|
|
632
|
+
return path, {**kwargs, **query_params} # merge kwargs + query_params
|
|
623
633
|
|
|
624
|
-
return
|
|
634
|
+
return clean_route, query_params
|
|
625
635
|
|
|
626
636
|
@staticmethod
|
|
627
637
|
def inspect_function(callback: Callable):
|
|
@@ -48,7 +48,7 @@ def run(
|
|
|
48
48
|
@route('/')
|
|
49
49
|
def home():
|
|
50
50
|
return pw.Template(template='Hello, world')
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
if __name__ == '__main__':
|
|
53
53
|
app.run()
|
|
54
54
|
```
|
|
@@ -94,7 +94,7 @@ def encode_header(headers: dict[str, Any], /,*ignore_headers: str):
|
|
|
94
94
|
|
|
95
95
|
if header not in set(map(lambda el: el.strip().lower(), ignore_headers)):
|
|
96
96
|
byte_headers.append((header.encode(), str(value).encode()))
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
return byte_headers
|
|
99
99
|
|
|
100
100
|
async def run_as_asgi(scope, receive, send, app: 'Pyweber', target: Callable = None): # pragma: no cover
|
|
@@ -108,7 +108,7 @@ async def run_as_asgi(scope, receive, send, app: 'Pyweber', target: Callable = N
|
|
|
108
108
|
message = await receive()
|
|
109
109
|
body += message.get("body", b"")
|
|
110
110
|
more_body = message.get("more_body", False)
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
client_info = scope.get('client', (None, 0))
|
|
113
113
|
request = Request(
|
|
114
114
|
headers=scope,
|
|
@@ -118,7 +118,7 @@ async def run_as_asgi(scope, receive, send, app: 'Pyweber', target: Callable = N
|
|
|
118
118
|
port=client_info[-1]
|
|
119
119
|
)
|
|
120
120
|
)
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
ws_server.protocol = 'uvicorn'
|
|
123
123
|
|
|
124
124
|
if not WS_RUNNING:
|
|
@@ -126,7 +126,7 @@ async def run_as_asgi(scope, receive, send, app: 'Pyweber', target: Callable = N
|
|
|
126
126
|
|
|
127
127
|
ws_server.app = app
|
|
128
128
|
app.ws_server = ws_server
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
if target and callable(target):
|
|
131
131
|
target(app)
|
|
132
132
|
|
|
@@ -143,7 +143,7 @@ async def run_as_asgi(scope, receive, send, app: 'Pyweber', target: Callable = N
|
|
|
143
143
|
headers = encode_header(response['headers'], 'set-cookie', 'code')
|
|
144
144
|
|
|
145
145
|
if app.cookies:
|
|
146
|
-
for cookie in app.cookies:
|
|
146
|
+
for cookie in app.cookies.values():
|
|
147
147
|
headers.append((b'set-cookie', cookie.encode()))
|
|
148
148
|
|
|
149
149
|
await send({
|
|
@@ -155,4 +155,4 @@ async def run_as_asgi(scope, receive, send, app: 'Pyweber', target: Callable = N
|
|
|
155
155
|
await send({
|
|
156
156
|
'type': 'http.response.body',
|
|
157
157
|
'body': response.response_content
|
|
158
|
-
})
|
|
158
|
+
})
|
|
@@ -476,19 +476,20 @@ class Pyweber(
|
|
|
476
476
|
try:
|
|
477
477
|
template = state_result.template
|
|
478
478
|
|
|
479
|
+
kwargs = {**self.request.body, **self.request.query_params, 'request': self.request} if self.request else {}
|
|
479
480
|
while callable(template) or isinstance(template, RedirectRoute):
|
|
480
|
-
|
|
481
|
+
kwargs = {**kwargs, **state_result.kwargs}
|
|
481
482
|
|
|
482
483
|
if callable(template):
|
|
483
|
-
kwargs =
|
|
484
|
-
|
|
485
|
-
**
|
|
486
|
-
|
|
484
|
+
kwargs = {
|
|
485
|
+
**kwargs,
|
|
486
|
+
**OpenApiProcessor.prepare_callback_kwargs(callback=state_result.callback, **kwargs)
|
|
487
|
+
}
|
|
487
488
|
|
|
488
489
|
template = await template(**kwargs) if inspect.iscoroutinefunction(template) else template(**kwargs)
|
|
489
490
|
|
|
490
491
|
if isinstance(template, RedirectRoute):
|
|
491
|
-
kwargs = {**
|
|
492
|
+
kwargs = {**kwargs, **template.kwargs}
|
|
492
493
|
redirect_path = self.build_route(route=template.route.full_route_with_params, **kwargs)
|
|
493
494
|
|
|
494
495
|
self._check_recursion(route=redirect_path)
|
|
@@ -570,6 +571,14 @@ class Pyweber(
|
|
|
570
571
|
methods=['post'],
|
|
571
572
|
content_type=ContentTypes.json
|
|
572
573
|
),
|
|
574
|
+
Route(
|
|
575
|
+
route='/_pyweber/check-cookies',
|
|
576
|
+
template={'message': 'OK'},
|
|
577
|
+
methods=['get'],
|
|
578
|
+
title='Get Cookies',
|
|
579
|
+
process_response=False,
|
|
580
|
+
content_type=ContentTypes.json
|
|
581
|
+
),
|
|
573
582
|
Route(
|
|
574
583
|
route='/docs',
|
|
575
584
|
template=StaticFilePath.pyweber_docs.value,
|
|
@@ -117,6 +117,7 @@ function connectWebSocket() {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (data.open) {
|
|
120
|
+
await update_cookies();
|
|
120
121
|
data.open.new_page
|
|
121
122
|
? window.open(data.open.path, '_blank')
|
|
122
123
|
: (window.location.href = data.open.path);
|
|
@@ -232,6 +233,13 @@ function connectWebSocket() {
|
|
|
232
233
|
return socket;
|
|
233
234
|
}
|
|
234
235
|
|
|
236
|
+
async function update_cookies() {
|
|
237
|
+
await fetch(`/_pyweber/check-cookies`, {
|
|
238
|
+
method: 'GET',
|
|
239
|
+
headers: { 'Content-Type': 'application/json' }
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
235
243
|
// ─── Envio de ficheiro via HTTP (mantém binário puro, sem compressão JSON) ────
|
|
236
244
|
async function send_file_chunk(file_id, status, data) {
|
|
237
245
|
await fetch(`/_pyweber/file_chunk?file_id=${file_id}&status=${status}`, {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/apple-touch-icon.png
RENAMED
|
File without changes
|
{pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/favicon-16x16.png
RENAMED
|
File without changes
|
{pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/favicon-32x32.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber/static/favicon/site.webmanifest
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260428}/pyweber.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|