tina4-python 0.2.26__tar.gz → 0.2.28__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 (46) hide show
  1. {tina4_python-0.2.26 → tina4_python-0.2.28}/PKG-INFO +3 -2
  2. {tina4_python-0.2.26 → tina4_python-0.2.28}/pyproject.toml +4 -2
  3. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Auth.py +9 -4
  4. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Router.py +31 -23
  5. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Webserver.py +55 -5
  6. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/__init__.py +11 -6
  7. tina4_python-0.2.26/tina4_python/.env +0 -7
  8. tina4_python-0.2.26/tina4_python/logs/debug.log +0 -1066
  9. tina4_python-0.2.26/tina4_python/secrets/domain.cert +0 -21
  10. tina4_python-0.2.26/tina4_python/secrets/private.key +0 -30
  11. tina4_python-0.2.26/tina4_python/secrets/public.key +0 -9
  12. tina4_python-0.2.26/tina4_python/test.db +0 -0
  13. {tina4_python-0.2.26 → tina4_python-0.2.28}/README.md +0 -0
  14. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Constant.py +0 -0
  15. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Database.py +0 -0
  16. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/DatabaseResult.py +0 -0
  17. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Debug.py +0 -0
  18. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Env.py +0 -0
  19. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Localization.py +0 -0
  20. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Messages.py +0 -0
  21. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/MiddleWare.py +0 -0
  22. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Migration.py +0 -0
  23. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Request.py +0 -0
  24. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Response.py +0 -0
  25. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Session.py +0 -0
  26. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/ShellColors.py +0 -0
  27. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Swagger.py +0 -0
  28. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Template.py +0 -0
  29. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/messages.pot +0 -0
  30. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/css/readme.md +0 -0
  31. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/favicon.ico +0 -0
  32. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/403.png +0 -0
  33. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/404.png +0 -0
  34. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/logo.png +0 -0
  35. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/readme.md +0 -0
  36. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/js/readme.md +0 -0
  37. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/js/tina4helper.js +0 -0
  38. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/swagger/index.html +0 -0
  39. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  40. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/templates/errors/403.twig +0 -0
  41. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/templates/errors/404.twig +0 -0
  42. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/templates/readme.md +0 -0
  43. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  44. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  45. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  46. {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tina4-python
3
- Version: 0.2.26
3
+ Version: 0.2.28
4
4
  Summary: Tina4Python - This is not another framework for Python
5
5
  Author: Andre van Zuydam
6
6
  Author-email: andrevanzuydam@gmail.com
@@ -9,7 +9,8 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
- Requires-Dist: Jinja2 (>=3.0.3,<4.0.0)
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: Jinja2 (>=3.1.4,<4.0.0)
13
14
  Requires-Dist: bcrypt (>=4.1.3,<5.0.0)
14
15
  Requires-Dist: cryptography (>=42.0.5,<43.0.0)
15
16
  Requires-Dist: libsass (>=0.22.0,<0.23.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "tina4-python"
3
- version = "0.2.26"
3
+ version = "0.2.28"
4
4
  description = "Tina4Python - This is not another framework for Python"
5
5
  authors = ["Andre van Zuydam <andrevanzuydam@gmail.com>"]
6
6
  readme = "README.md"
@@ -9,7 +9,7 @@ readme = "README.md"
9
9
 
10
10
  [tool.poetry.dependencies]
11
11
  python = "^3.10"
12
- Jinja2 = "^3.0.3"
12
+ Jinja2 = "^3.1.4"
13
13
  mypy = "^0.991"
14
14
  libsass = "^0.22.0"
15
15
  python-dotenv = "^1.0.1"
@@ -22,6 +22,8 @@ bcrypt = "^4.1.3"
22
22
  flake8 = "^7.0.0"
23
23
  pytest = "^7.2.0"
24
24
  jurigged = "^0.5.3"
25
+ safety = "^3.2.8"
26
+ ruff = "^0.7.0"
25
27
 
26
28
  [build-system]
27
29
  requires = ["poetry-core>=1.0.0"]
@@ -127,12 +127,17 @@ class Auth:
127
127
  with open(self.self_signed, "wb") as f:
128
128
  f.write(cert.public_bytes(serialization.Encoding.PEM))
129
129
 
130
- def get_token(self, payload_data):
130
+ def get_token(self, payload_data, expiry_minutes=0):
131
131
  private_key = self.load_private_key()
132
132
  now = datetime.datetime.now()
133
- token_limit_minutes = int(os.environ.get("TINA4_TOKEN_LIMIT", 2))
134
- expiry_time = now + datetime.timedelta(minutes=token_limit_minutes)
135
- payload_data["expires"] = expiry_time.isoformat()
133
+
134
+ if not "expires" in payload_data:
135
+ token_limit_minutes = int(os.environ.get("TINA4_TOKEN_LIMIT", 2))
136
+ if expiry_minutes != 0:
137
+ token_limit_minutes = expiry_minutes
138
+ expiry_time = now + datetime.timedelta(minutes=token_limit_minutes)
139
+ payload_data["expires"] = expiry_time.isoformat()
140
+
136
141
  token = jwt.encode(
137
142
  payload=payload_data,
138
143
  key=private_key,
@@ -65,9 +65,9 @@ class Router:
65
65
 
66
66
  Response.headers = {}
67
67
  Response.content = ""
68
- Response.http_code = Constant.HTTP_OK
68
+ Response.http_code = Constant.HTTP_NOT_FOUND
69
69
  Response.content_type = Constant.TEXT_HTML
70
- result = None
70
+ result = Response
71
71
 
72
72
  Debug("Root Path " + tina4_python.root_path + " " + url, method, Constant.TINA4_LOG_DEBUG)
73
73
  tina4_python.tina4_current_request["url"] = url
@@ -165,6 +165,11 @@ class Router:
165
165
 
166
166
  result = await router_response(request=Request, response=Response.Response)
167
167
 
168
+ # we have found a result ... make sure we reflect this if the user didn't actually put the correct http response code in
169
+ if result is not None:
170
+ if result.http_code == Constant.HTTP_NOT_FOUND:
171
+ result.http_code = Constant.HTTP_OK
172
+
168
173
  if "middleware" in route:
169
174
  middleware_runner = MiddleWare(route["middleware"]["class"])
170
175
 
@@ -190,36 +195,39 @@ class Router:
190
195
 
191
196
  break
192
197
 
193
- if result is None:
194
- if old_stdout is not None:
195
- sys.stdout = old_stdout
198
+ if result is None and old_stdout is not None:
199
+ sys.stdout = old_stdout
200
+ if buffer.getvalue() != "":
201
+ try:
202
+ return Response.Response(json.loads(buffer.getvalue()), Constant.HTTP_OK, Constant.APPLICATION_JSON)
203
+ except:
204
+ return Response.Response(buffer.getvalue(), Constant.HTTP_OK, Constant.TEXT_HTML)
196
205
  else:
197
- sys.stdout = buffer = io.StringIO()
198
-
199
- try:
200
- return Response.Response(json.loads(buffer.getvalue()), Constant.HTTP_OK, Constant.APPLICATION_JSON)
201
- except:
202
- return Response.Response(buffer.getvalue(), Constant.HTTP_OK, Constant.TEXT_HTML)
206
+ result = Response
207
+ result.http_code = Constant.HTTP_NOT_FOUND
203
208
 
204
209
  # If no route is matched, serve 404
205
210
  if result.http_code == Constant.HTTP_NOT_FOUND:
206
211
  # Serve twigs if the files exist
212
+ twig_files = []
207
213
  if url == "/":
208
- twig_file = "index.twig"
214
+ twig_files.append("index.twig")
209
215
  else:
210
- twig_file = url + ".twig"
216
+ twig_files.append(url + ".twig")
217
+ twig_files.append(url + "index.twig")
211
218
 
212
219
  # see if we can find the twig file
213
- if os.path.isfile(tina4_python.root_path + os.sep + "src" + os.sep + "templates" + os.sep + twig_file):
214
- Debug("Looking for twig file",
215
- tina4_python.root_path + os.sep + "src" + os.sep + "templates" + os.sep + twig_file,
216
- Constant.TINA4_LOG_DEBUG)
217
-
218
- result.headers["Cache-Control"] = "max-age=-1, public"
219
- result.headers["Pragma"] = "no-cache"
220
- content = Template.render_twig_template(twig_file, {"request": tina4_python.tina4_current_request})
221
- if content != "":
222
- return Response.Response(content, Constant.HTTP_OK, Constant.TEXT_HTML, result.headers)
220
+ for twig_file in twig_files:
221
+ if os.path.isfile(tina4_python.root_path + os.sep + "src" + os.sep + "templates" + os.sep + twig_file):
222
+ Debug("Looking for twig file",
223
+ tina4_python.root_path + os.sep + "src" + os.sep + "templates" + os.sep + twig_file,
224
+ Constant.TINA4_LOG_DEBUG)
225
+
226
+ result.headers["Cache-Control"] = "max-age=-1, public"
227
+ result.headers["Pragma"] = "no-cache"
228
+ content = Template.render_twig_template(twig_file, {"request": tina4_python.tina4_current_request})
229
+ if content != "":
230
+ return Response.Response(content, Constant.HTTP_OK, Constant.TEXT_HTML, result.headers)
223
231
 
224
232
  if result.http_code == Constant.HTTP_NOT_FOUND:
225
233
  content = Template.render_twig_template(
@@ -8,7 +8,7 @@ import asyncio
8
8
  import base64
9
9
  import json
10
10
  import os
11
- import sys
11
+ import re
12
12
  from urllib.parse import unquote_plus
13
13
  from urllib.parse import urlparse, parse_qsl
14
14
  import tina4_python
@@ -16,6 +16,13 @@ from tina4_python import Constant
16
16
  from tina4_python.Constant import HTTP_REDIRECT
17
17
  from tina4_python.Session import Session
18
18
 
19
+ def is_int(v):
20
+ try:
21
+ f=int(v)
22
+ except ValueError:
23
+ return False
24
+ return True
25
+
19
26
 
20
27
  class Webserver:
21
28
  async def get_content_length(self):
@@ -102,23 +109,66 @@ class Webserver:
102
109
  headers = await self.get_headers(headers, self.response_protocol, Constant.HTTP_OK)
103
110
  return headers
104
111
 
105
-
106
112
  params = dict(parse_qsl(urlparse(self.path).query, keep_blank_values=True))
107
113
 
114
+ new_params = {}
115
+ new_params.update(params)
116
+
117
+ for key, value in params.items():
118
+ regex = r"(\w+)"
119
+ matches = re.finditer(regex, key)
120
+ start_var = new_params
121
+ var_names = []
122
+ for matchNum, match in enumerate(matches, start=0):
123
+ if is_int(match.group()):
124
+ var_names.append(int(match.group()))
125
+ else:
126
+ var_names.append(match.group())
127
+
128
+ counter = 0
129
+
130
+ while counter < len(var_names):
131
+ var_name = var_names[counter]
132
+ if not is_int(var_name):
133
+ if isinstance(start_var, dict) and var_name in start_var:
134
+ start_var = start_var[var_name]
135
+ else:
136
+ if counter+1 < len(var_names) and is_int(var_names[counter+1]) :
137
+ if var_name not in start_var:
138
+ start_var[var_name] = []
139
+ start_var = start_var[var_name]
140
+ else:
141
+ if counter-1 > 0 and is_int(var_names[counter-1]):
142
+ index = int(var_names[counter-1])
143
+ new_value = {var_name: value}
144
+ if index in range(len(start_var)):
145
+ start_var[index].update(new_value)
146
+ else:
147
+ while len(start_var) < index:
148
+ start_var.append({})
149
+ start_var.append(new_value)
150
+ start_var = start_var[index]
151
+ else:
152
+ if isinstance(start_var, dict):
153
+ start_var[var_name] = value
154
+ start_var = start_var[var_name]
155
+
156
+ counter += 1
157
+
158
+ params.update(new_params)
108
159
  content_length = await self.get_content_length()
109
160
  if method != Constant.TINA4_GET:
110
161
  body = await self.get_content_body(content_length)
111
162
  else:
112
163
  body = None
113
164
 
114
- request = {"params": params, "body": body, "raw_data": self.request, "url": self.path, "headers": self.lowercase_headers, "raw_request": self.request_raw, "raw_content": self.content_raw}
165
+ request = {"params": params, "body": body, "raw_data": self.request, "url": self.path,
166
+ "headers": self.lowercase_headers, "raw_request": self.request_raw, "raw_content": self.content_raw}
115
167
 
116
168
  tina4_python.tina4_current_request = request
117
169
 
118
170
  response = await self.router_handler.resolve(method, self.path, request, self.lowercase_headers, self.session)
119
171
 
120
-
121
-
122
172
  if HTTP_REDIRECT != response.http_code:
123
173
  self.send_header("Access-Control-Allow-Origin", "*", headers)
124
174
  self.send_header("Access-Control-Allow-Headers",
@@ -45,7 +45,6 @@ load_env(environment)
45
45
 
46
46
  print(ShellColors.bright_yellow + "Setting debug mode", os.getenv("TINA4_DEBUG_LEVEL"), ShellColors.end)
47
47
 
48
-
49
48
  if importlib.util.find_spec("jurigged"):
50
49
  import jurigged
51
50
 
@@ -61,7 +60,6 @@ localize()
61
60
  Debug(Messages.MSG_ASSUMING_ROOT_PATH.format(root_path=root_path, library_path=library_path),
62
61
  Constant.TINA4_LOG_INFO)
63
62
 
64
-
65
63
  tina4_routes = {}
66
64
  tina4_current_request = {}
67
65
  tina4_api_key = None
@@ -139,6 +137,7 @@ if os.path.exists(root_path + os.sep + "src" + os.sep + "app"):
139
137
  else:
140
138
  Debug("Missing src/app folder", Constant.TINA4_LOG_WARNING)
141
139
 
140
+
142
141
  # compile sass
143
142
  def compile_scss():
144
143
  try:
@@ -168,23 +167,28 @@ if os.path.exists(root_path + os.sep + "src" + os.sep + "scss"):
168
167
  else:
169
168
  Debug("Missing scss folder", Constant.TINA4_LOG_WARNING)
170
169
 
170
+
171
171
  # end compile sass
172
172
 
173
173
 
174
174
  def file_get_contents(file_path):
175
175
  return Path(file_path).read_text()
176
176
 
177
+
177
178
  # Add swagger routes
178
- @get("/swagger/swagger.json")
179
+ @get(os.getenv("SWAGGER_ROUTE", "/swagger")+"/swagger.json")
179
180
  async def get_swagger_json(request, response):
180
181
  json = Swagger.get_json(request)
181
182
  return response(json)
182
183
 
183
- @get("/swagger")
184
+
185
+ @get(os.getenv("SWAGGER_ROUTE", "/swagger"))
184
186
  async def get_swagger(request, response):
185
- html = file_get_contents(root_path + os.sep +"src"+os.sep+"public"+ os.sep+"swagger"+os.sep+"index.html")
187
+ html = file_get_contents(
188
+ root_path + os.sep + "src" + os.sep + "public" + os.sep + "swagger" + os.sep + "index.html")
186
189
  return response(html)
187
190
 
191
+
188
192
  def webserver(host_name, port):
189
193
  web_server = Webserver(host_name, int(port)) # HTTPServer((host_name, int(port)), Webserver)
190
194
  web_server.router_handler = Router()
@@ -204,7 +208,8 @@ def run_web_server(in_hostname="localhost", in_port=7145):
204
208
  Debug(Messages.MSG_STARTING_WEBSERVER.format(port=in_port), Constant.TINA4_LOG_INFO)
205
209
  webserver(in_hostname, in_port)
206
210
 
207
- if os.getenv('TINA4_DEFAULT_WEBSERVER', 'True') == 'True' :
211
+
212
+ if os.getenv('TINA4_DEFAULT_WEBSERVER', 'True') == 'True':
208
213
  if importlib.util.find_spec("jurigged"):
209
214
  Debug("Jurigged enabled", Constant.TINA4_LOG_INFO)
210
215
  jurigged.watch("./")
@@ -1,7 +0,0 @@
1
- # Project Settings
2
- PROJECT_NAME="My Project"
3
- VERSION=1.0.0
4
- TINA4_LANGUAGE=en
5
- TINA4_SECRET=ABCDEF
6
- TINA4_DEBUG_LEVEL=[TINA4_LOG_ALL]
7
- API_KEY=f9ca99e18c7bce9c6852df4257a3280b