pyweber 1.2.0.dev20260426__tar.gz → 1.2.0.dev20260427__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.
Files changed (88) hide show
  1. {pyweber-1.2.0.dev20260426/pyweber.egg-info → pyweber-1.2.0.dev20260427}/PKG-INFO +1 -1
  2. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyproject.toml +1 -1
  3. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/request.py +35 -32
  4. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/response.py +1 -1
  5. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/routes.py +15 -5
  6. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/pyweber/pyweber.py +15 -6
  7. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/js.js +8 -0
  8. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427/pyweber.egg-info}/PKG-INFO +1 -1
  9. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/LICENSE +0 -0
  10. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/MANIFEST.in +0 -0
  11. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/README.md +0 -0
  12. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/__init__.py +0 -0
  13. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/admin/index.html +0 -0
  14. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/admin/src/script.js +0 -0
  15. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/admin/src/style.css +0 -0
  16. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/cli/__init__.py +0 -0
  17. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/cli/commands.py +0 -0
  18. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/components/__init__.py +0 -0
  19. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/components/form.py +0 -0
  20. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/components/general.py +0 -0
  21. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/components/input.py +0 -0
  22. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/config/__init__.py +0 -0
  23. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/config/config.py +0 -0
  24. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/connection/__init__.py +0 -0
  25. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/connection/http.py +0 -0
  26. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/connection/reload.py +0 -0
  27. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/connection/selector.py +0 -0
  28. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/connection/session.py +0 -0
  29. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/connection/websocket.py +0 -0
  30. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/core/__init__.py +0 -0
  31. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/core/element.py +0 -0
  32. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/core/events.py +0 -0
  33. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/core/template.py +0 -0
  34. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/core/window.py +0 -0
  35. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/__init__.py +0 -0
  36. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/cookies.py +0 -0
  37. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/create_app.py +0 -0
  38. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/element.py +0 -0
  39. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/error_pages.py +0 -0
  40. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/field.py +0 -0
  41. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/field_storage.py +0 -0
  42. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/file.py +0 -0
  43. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/file_stream.py +0 -0
  44. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/headers.py +0 -0
  45. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/middleware.py +0 -0
  46. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/openapi.py +0 -0
  47. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/run.py +0 -0
  48. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/strem_stats.py +0 -0
  49. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/task_manager.py +0 -0
  50. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/template_diff.py +0 -0
  51. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/models/ws_message.py +0 -0
  52. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/pyweber/__init__.py +0 -0
  53. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/.gitignore +0 -0
  54. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/config.toml +0 -0
  55. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/css.css +0 -0
  56. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/docs.html +0 -0
  57. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/android-chrome-192x192.png +0 -0
  58. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/android-chrome-512x512.png +0 -0
  59. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/apple-touch-icon.png +0 -0
  60. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/favicon-16x16.png +0 -0
  61. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/favicon-32x32.png +0 -0
  62. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/favicon.ico +0 -0
  63. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/pyweber.png +0 -0
  64. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/favicon/site.webmanifest +0 -0
  65. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/handlers.js +0 -0
  66. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/html.html +0 -0
  67. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/html401.html +0 -0
  68. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/html404.html +0 -0
  69. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/html500.html +0 -0
  70. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/loading.html +0 -0
  71. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/main.py +0 -0
  72. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/pyweber.css +0 -0
  73. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/static/update.py +0 -0
  74. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/utils/__init__.py +0 -0
  75. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/utils/exceptions.py +0 -0
  76. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/utils/loads.py +0 -0
  77. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/utils/types.py +0 -0
  78. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber/utils/utils.py +0 -0
  79. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber.egg-info/SOURCES.txt +0 -0
  80. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber.egg-info/dependency_links.txt +0 -0
  81. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber.egg-info/entry_points.txt +0 -0
  82. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber.egg-info/requires.txt +0 -0
  83. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/pyweber.egg-info/top_level.txt +0 -0
  84. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/setup.cfg +0 -0
  85. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/tests/test_config.py +0 -0
  86. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/tests/test_cookies.py +0 -0
  87. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/tests/test_response.py +0 -0
  88. {pyweber-1.2.0.dev20260426 → pyweber-1.2.0.dev20260427}/tests/test_template_diff.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyweber
3
- Version: 1.2.0.dev20260426
3
+ Version: 1.2.0.dev20260427
4
4
  Summary: A lightweight Python framework for building and managing web applications.
5
5
  Author-email: DevPythonMZ <pypi.dev@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyweber"
7
- version = "1.2.0.dev20260426"
7
+ version = "1.2.0.dev20260427"
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,63 +67,63 @@ 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
129
  return {cookie.split('=')[0]: cookie.split('=')[-1] for cookie in self.headers.get('cookie', '').split(';') if cookie}
@@ -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.path,
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 = route.strip('/').split('/')
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(route) == 1:
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 route, kwargs
634
+ return clean_route, query_params
625
635
 
626
636
  @staticmethod
627
637
  def inspect_function(callback: Callable):
@@ -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
- request_params = {**self.request.body, **self.request.query_params, 'request': self.request} if self.request else {}
481
+ kwargs = {**kwargs, **state_result.kwargs}
481
482
 
482
483
  if callable(template):
483
- kwargs = OpenApiProcessor.prepare_callback_kwargs(
484
- callback=state_result.callback,
485
- **{**state_result.kwargs, **request_params}
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 = {**state_result.kwargs, **request_params, **template.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}`, {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyweber
3
- Version: 1.2.0.dev20260426
3
+ Version: 1.2.0.dev20260427
4
4
  Summary: A lightweight Python framework for building and managing web applications.
5
5
  Author-email: DevPythonMZ <pypi.dev@gmail.com>
6
6
  License-Expression: MIT