tina4-python 0.2.109__tar.gz → 0.2.111__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 (48) hide show
  1. {tina4_python-0.2.109 → tina4_python-0.2.111}/PKG-INFO +1 -1
  2. {tina4_python-0.2.109 → tina4_python-0.2.111}/pyproject.toml +1 -1
  3. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Database.py +5 -2
  4. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/ORM.py +5 -5
  5. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Router.py +12 -1
  6. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Session.py +82 -0
  7. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Webserver.py +22 -6
  8. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/__init__.py +3 -2
  9. {tina4_python-0.2.109 → tina4_python-0.2.111}/.gitignore +0 -0
  10. {tina4_python-0.2.109 → tina4_python-0.2.111}/README.md +0 -0
  11. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Auth.py +0 -0
  12. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Constant.py +0 -0
  13. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/DatabaseResult.py +0 -0
  14. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/DatabaseTypes.py +0 -0
  15. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Debug.py +0 -0
  16. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Env.py +0 -0
  17. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Localization.py +0 -0
  18. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Messages.py +0 -0
  19. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/MiddleWare.py +0 -0
  20. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Migration.py +0 -0
  21. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Queue.py +0 -0
  22. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Request.py +0 -0
  23. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Response.py +0 -0
  24. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/ShellColors.py +0 -0
  25. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Swagger.py +0 -0
  26. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Template.py +0 -0
  27. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/Websocket.py +0 -0
  28. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/messages.pot +0 -0
  29. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/css/readme.md +0 -0
  30. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/favicon.ico +0 -0
  31. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/images/403.png +0 -0
  32. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/images/404.png +0 -0
  33. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/images/500.png +0 -0
  34. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/images/logo.png +0 -0
  35. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/images/readme.md +0 -0
  36. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/js/readme.md +0 -0
  37. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/js/reconnecting-websocket.js +0 -0
  38. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/js/tina4helper.js +0 -0
  39. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/swagger/index.html +0 -0
  40. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  41. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/templates/errors/403.twig +0 -0
  42. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/templates/errors/404.twig +0 -0
  43. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/templates/errors/500.twig +0 -0
  44. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/templates/readme.md +0 -0
  45. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  46. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  47. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  48. {tina4_python-0.2.109 → tina4_python-0.2.111}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 0.2.109
3
+ Version: 0.2.111
4
4
  Summary: Tina4Python - This is not another framework for Python
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  Requires-Python: <4.0,>=3.12
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tina4-python"
3
- version = "0.2.109"
3
+ version = "0.2.111"
4
4
  description = "Tina4Python - This is not another framework for Python"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam",email = "andrevanzuydam@gmail.com"}
@@ -141,7 +141,6 @@ class Database:
141
141
  :return: bool : True if table exists, else False
142
142
  """
143
143
 
144
- sql = ""
145
144
  if self.database_engine == MSSQL:
146
145
  sql = "select count(*) as count_table from sys.tables WHERE name = '"+table_name.upper()+"'"
147
146
  elif self.database_engine == SQLITE:
@@ -158,7 +157,11 @@ class Database:
158
157
  else:
159
158
  return False
160
159
 
161
- record = self.fetch_one(sql)
160
+ try:
161
+ record = self.fetch_one(sql)
162
+ except Exception as e:
163
+ raise Exception (f"Error checking if table {table_name} exists: "+str(e))
164
+
162
165
  if record:
163
166
  if record["count_table"] > 0:
164
167
  return True
@@ -330,16 +330,16 @@ class ORM:
330
330
  self.__populate_orm({})
331
331
 
332
332
  # Debug("Checking for", self.__table_name__, TINA4_LOG_INFO)
333
- if self.__dba__:
333
+ if self.__dba__ is not None:
334
334
  self.__table_exists = self.__dba__.table_exists(self.__table_name__)
335
335
  if not self.__table_exists:
336
336
  sql = self.__create_table__(self.__table_name__)
337
337
  filename = root_path + os.sep + "migrations" + os.sep + "__" + self.__table_name__ + ".sql"
338
338
  os.makedirs(os.path.dirname(filename), exist_ok=True)
339
- with open(filename, "w") as f:
340
- f.write(sql)
341
- f.close()
342
- # Debug("Table Exists", self.__table_exists, TINA4_LOG_INFO)
339
+ #with open(filename, "w") as f:
340
+ # f.write(sql)
341
+ # f.close()
342
+ Debug.warning("Create Table ? ", sql)
343
343
  else:
344
344
  self.__table_exists = False
345
345
 
@@ -143,6 +143,7 @@ class Router:
143
143
  Request.headers = headers # Add the headers
144
144
  Request.params = request["params"]
145
145
  Request.body = request["body"] if "body" in request else None
146
+ Request.files = request["files"] if "files" in request else None
146
147
  Request.session = session
147
148
  Request.raw_data = request["raw_data"] if "raw_data" in request else None
148
149
  Request.raw_request = request["raw_request"] if "raw_request" in request else None
@@ -151,6 +152,7 @@ class Router:
151
152
  Request.transport = request["transport"] if "transport" in request else None
152
153
  Request.asgi_response = request["asgi_response"] if "asgi_response" in request else None
153
154
 
155
+
154
156
  tina4_python.tina4_current_request = Request
155
157
 
156
158
  old_stdout = sys.stdout # Memorize the default stdout stream
@@ -166,7 +168,16 @@ class Router:
166
168
  Request, Response = middleware_runner.call_before_methods(Request, Response)
167
169
  Request, Response = middleware_runner.call_any_methods(Request, Response)
168
170
 
169
- result = await router_response(request=Request, response=Response.Response)
171
+ try:
172
+ result = await router_response(request=Request, response=Response.Response)
173
+ except Exception as e:
174
+ error_string = tina4_python.global_exception_handler(e)
175
+ if Constant.TINA4_LOG_DEBUG in os.getenv("TINA4_DEBUG_LEVEL") or Constant.TINA4_LOG_ALL in os.getenv("TINA4_DEBUG_LEVEL"):
176
+ html = Template.render_twig_template("errors/500.twig",
177
+ {"server": {"url": url}, "error_message": error_string})
178
+ return Response.Response(html, Constant.HTTP_SERVER_ERROR, Constant.TEXT_HTML)
179
+ else:
180
+ return Response.Response(error_string, Constant.HTTP_SERVER_ERROR, Constant.TEXT_HTML)
170
181
 
171
182
  # we have found a result ... make sure we reflect this if the user didn't actually put the correct http response code in
172
183
  if result is not None:
@@ -179,6 +179,88 @@ class SessionRedisHandler(SessionHandler):
179
179
  Debug("Session save failure", str(e), Constant.TINA4_LOG_ERROR)
180
180
  return False
181
181
 
182
+ class SessionValkeyHandler(SessionHandler):
183
+
184
+ @staticmethod
185
+ def __init_valkey():
186
+ try:
187
+ valkey = importlib.import_module("valkey")
188
+ except Exception as e:
189
+ Debug("Valkey not installed, install with pip/uv", str(e), Constant.TINA4_LOG_ERROR)
190
+ sys.exit(1)
191
+
192
+ params = {
193
+ "host": os.getenv("TINA4_SESSION_VALKEY_HOST", "localhost"),
194
+ "port": int(os.getenv("TINA4_SESSION_VALKEY_PORT", 6379)),
195
+ "decode_responses": True
196
+ }
197
+ if os.getenv("TINA4_SESSION_VALKEY_SECRET", ""):
198
+ params["password"] = os.getenv("TINA4_SESSION_VALKEY_SECRET", "")
199
+ params["username"] = os.getenv("TINA4_SESSION_VALKEY_USER", "default")
200
+
201
+ if os.getenv("TINA4_SESSION_VALKEY_SSL", "False").upper() == "TRUE":
202
+ params["ssl"] = True
203
+
204
+ valkey_instance = valkey.Valkey(**params)
205
+
206
+ return valkey_instance
207
+
208
+ """
209
+ Session Valkey Handler
210
+ """
211
+ @staticmethod
212
+ def load(session, _hash):
213
+ """
214
+ Loads the Valkey session
215
+ :param session:
216
+ :param _hash:
217
+ :return:
218
+ """
219
+ try:
220
+ session.session_hash = _hash
221
+ r = SessionValkeyHandler.__init_valkey()
222
+ token = r.get(_hash)
223
+ if tina4_python.tina4_auth.valid(token):
224
+ payload = tina4_python.tina4_auth.get_payload(token)
225
+ for key in payload:
226
+ if key != "expires":
227
+ session.set(key, payload[key])
228
+ else:
229
+ Debug("Session expired, starting a new one", Constant.TINA4_LOG_DEBUG)
230
+ session.start(_hash)
231
+ except Exception:
232
+ Debug("Valkey not available, sessions will fail", Constant.TINA4_LOG_ERROR)
233
+
234
+
235
+ @staticmethod
236
+ def close(session):
237
+ """
238
+ Closes the Valkey session
239
+ :param session:
240
+ :return:
241
+ """
242
+ r = SessionValkeyHandler.__init_valkey()
243
+ try:
244
+ r.set(session.session_hash, "")
245
+ return True
246
+ except Exception:
247
+ return False
248
+
249
+ @staticmethod
250
+ def save(session):
251
+ """
252
+ Saves the Valkey session
253
+ :param session:
254
+ :return:
255
+ """
256
+ r = SessionValkeyHandler.__init_valkey()
257
+ try:
258
+ token = tina4_python.tina4_auth.get_token(payload_data=session.session_values)
259
+ r.set(session.session_hash, token)
260
+ return True
261
+ except Exception as e:
262
+ Debug("Session save failure", str(e), Constant.TINA4_LOG_ERROR)
263
+ return False
182
264
 
183
265
  class Session:
184
266
 
@@ -64,6 +64,7 @@ class Webserver:
64
64
  content = b"\r\n" + content
65
65
  data_array = content.split(str.encode(boundary))
66
66
  body = {}
67
+ files = {}
67
68
  for data in data_array:
68
69
  data = data.split(b"\r\n\r\n")
69
70
  data_names = data[0].decode("utf-8").split("; ")
@@ -91,10 +92,24 @@ class Webserver:
91
92
  if "Content-Type" in meta_data:
92
93
  content_type = meta_data["Content-Type"]
93
94
 
94
- body[key_name] = {"file_name": file_name, "content_type": content_type,
95
- "content": base64.encodebytes(data_value).decode("utf-8").replace(
96
- "\n", "")}
97
- return body
95
+
96
+ if key_name in body:
97
+ body[key_name] = [body[key_name]]
98
+ body[key_name].append({"file_name": file_name, "content_type": content_type,"content": base64.encodebytes(data_value).decode("utf-8").replace(
99
+ "\n", "")})
100
+ else:
101
+ body[key_name] = {"file_name": file_name, "content_type": content_type,"content": base64.encodebytes(data_value).decode("utf-8").replace(
102
+ "\n", "")}
103
+
104
+ if key_name in files:
105
+ files[key_name] = [files[key_name]]
106
+ files[key_name].append({"file_name": file_name, "content_type": content_type,"content": base64.encodebytes(data_value).decode("utf-8").replace(
107
+ "\n", "")})
108
+ else:
109
+ files[key_name] = {"file_name": file_name, "content_type": content_type,"content": base64.encodebytes(data_value).decode("utf-8").replace(
110
+ "\n", "")}
111
+
112
+ return body, files
98
113
 
99
114
  return {"data": base64.encodebytes(content).decode("utf-8").replace("\n", "")}
100
115
 
@@ -181,11 +196,12 @@ class Webserver:
181
196
 
182
197
  content_length = await self.get_content_length()
183
198
  if method != Constant.TINA4_GET:
184
- body = await self.get_content_body(content_length)
199
+ body, files = await self.get_content_body(content_length)
185
200
  else:
186
201
  body = None
202
+ files = None
187
203
 
188
- request = {"params": params, "body": body, "raw_data": self.request, "url": self.path, "session": self.session,
204
+ request = {"params": params, "body": body, "files": files, "raw_data": self.request, "url": self.path, "session": self.session,
189
205
  "headers": self.lowercase_headers, "raw_request": self.request_raw, "raw_content": self.content_raw,
190
206
  "transport": transport, "asgi_response": asgi_response}
191
207
 
@@ -82,7 +82,7 @@ def global_exception_handler(exception):
82
82
  or debug_level in os.getenv("TINA4_DEBUG_LEVEL", [Constant.TINA4_LOG_ALL])):
83
83
  pass
84
84
  else:
85
- error_string = ""
85
+ error_string = "An exception happened"
86
86
  return error_string
87
87
 
88
88
  def start_in_thread(target, exception_hook=None):
@@ -279,7 +279,6 @@ async def app(scope, receive, send):
279
279
  webserver.content_raw = b""
280
280
  webserver.content_length = parsed_headers_lowercase["content-length"]
281
281
  webserver.router_handler = Router()
282
- webserver.session = Session
283
282
 
284
283
  cookie_list = {}
285
284
  if "cookie" in webserver.lowercase_headers:
@@ -340,6 +339,7 @@ def webserver(host_name, port):
340
339
  :return:
341
340
  """
342
341
  if os.getenv('TINA4_DEFAULT_WEBSERVER', 'FALSE').upper() == 'TRUE':
342
+ Debug("Using default webserver", Constant.TINA4_LOG_INFO)
343
343
  # runs the built-in webserver (websockets) don't work on windows
344
344
  web_server = Webserver(host_name, int(port)) # HTTPServer((host_name, int(port)), Webserver)
345
345
  web_server.router_handler = Router()
@@ -354,6 +354,7 @@ def webserver(host_name, port):
354
354
  web_server.server_close()
355
355
  else:
356
356
  # Runs a hyper corn server
357
+ Debug("Using hypercorn webserver", Constant.TINA4_LOG_INFO)
357
358
  try:
358
359
  from hypercorn.config import Config
359
360
  from hypercorn.asyncio import serve
File without changes