tina4-python 0.2.188__tar.gz → 0.2.190__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 (73) hide show
  1. tina4_python-0.2.190/PKG-INFO +264 -0
  2. tina4_python-0.2.190/README.md +243 -0
  3. {tina4_python-0.2.188 → tina4_python-0.2.190}/pyproject.toml +1 -1
  4. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Auth.py +2 -1
  5. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/CLAUDE.md +300 -10
  6. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/CRUD.py +8 -4
  7. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Database.py +10 -8
  8. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Localization.py +26 -24
  9. tina4_python-0.2.190/tina4_python/Messages.py +103 -0
  10. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Migration.py +7 -8
  11. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Response.py +8 -5
  12. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Router.py +3 -3
  13. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Session.py +123 -2
  14. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Template.py +5 -0
  15. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Webserver.py +4 -2
  16. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Websocket.py +4 -2
  17. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/cli.py +29 -29
  18. tina4_python-0.2.190/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  19. tina4_python-0.2.190/tina4_python/translations/af/LC_MESSAGES/messages.po +233 -0
  20. tina4_python-0.2.190/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  21. tina4_python-0.2.190/tina4_python/translations/en/LC_MESSAGES/messages.po +233 -0
  22. tina4_python-0.2.190/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  23. tina4_python-0.2.190/tina4_python/translations/es/LC_MESSAGES/messages.po +233 -0
  24. tina4_python-0.2.190/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  25. tina4_python-0.2.190/tina4_python/translations/fr/LC_MESSAGES/messages.po +233 -0
  26. tina4_python-0.2.190/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  27. tina4_python-0.2.190/tina4_python/translations/ja/LC_MESSAGES/messages.po +233 -0
  28. tina4_python-0.2.190/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  29. tina4_python-0.2.190/tina4_python/translations/zh/LC_MESSAGES/messages.po +233 -0
  30. tina4_python-0.2.188/PKG-INFO +0 -117
  31. tina4_python-0.2.188/README.md +0 -96
  32. tina4_python-0.2.188/tina4_python/Messages.py +0 -30
  33. tina4_python-0.2.188/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  34. tina4_python-0.2.188/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -80
  35. tina4_python-0.2.188/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  36. tina4_python-0.2.188/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -84
  37. {tina4_python-0.2.188 → tina4_python-0.2.190}/.gitignore +0 -0
  38. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Api.py +0 -0
  39. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Constant.py +0 -0
  40. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/DatabaseResult.py +0 -0
  41. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/DatabaseTypes.py +0 -0
  42. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Debug.py +0 -0
  43. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/DevReload.py +0 -0
  44. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Env.py +0 -0
  45. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/FieldTypes.py +0 -0
  46. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/HtmlElement.py +0 -0
  47. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/MiddleWare.py +0 -0
  48. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/ORM.py +0 -0
  49. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Queue.py +0 -0
  50. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Request.py +0 -0
  51. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/ShellColors.py +0 -0
  52. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Swagger.py +0 -0
  53. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/Testing.py +0 -0
  54. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/WSDL.py +0 -0
  55. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/__init__.py +0 -0
  56. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/messages.pot +0 -0
  57. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/css/readme.md +0 -0
  58. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/favicon.ico +0 -0
  59. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/images/403.png +0 -0
  60. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/images/404.png +0 -0
  61. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/images/500.png +0 -0
  62. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/images/logo.png +0 -0
  63. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/images/readme.md +0 -0
  64. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/js/readme.md +0 -0
  65. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/js/reconnecting-websocket.js +0 -0
  66. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/js/tina4helper.js +0 -0
  67. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/swagger/index.html +0 -0
  68. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  69. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/templates/components/crud.twig +0 -0
  70. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/templates/errors/403.twig +0 -0
  71. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/templates/errors/404.twig +0 -0
  72. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/templates/errors/500.twig +0 -0
  73. {tina4_python-0.2.188 → tina4_python-0.2.190}/tina4_python/templates/readme.md +0 -0
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: tina4-python
3
+ Version: 0.2.190
4
+ Summary: Tina4Python - This is not another framework for Python
5
+ Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
+ Requires-Python: <4.0,>=3.12
7
+ Requires-Dist: asyncer>=0.0.8
8
+ Requires-Dist: bcrypt<5.0.0,>=4.2.1
9
+ Requires-Dist: cryptography<45.0.0,>=44.0.0
10
+ Requires-Dist: hypercorn>=0.18.0
11
+ Requires-Dist: jinja2<4.0.0,>=3.1.5
12
+ Requires-Dist: jurigged>=0.6.0
13
+ Requires-Dist: libsass<0.24.0,>=0.23.0
14
+ Requires-Dist: litequeue<0.10,>=0.9
15
+ Requires-Dist: pyjwt<3.0.0,>=2.10.1
16
+ Requires-Dist: python-dotenv<2.0.0,>=1.0.1
17
+ Requires-Dist: requests>=2.32.5
18
+ Requires-Dist: simple-websocket<2.0.0,>=1.1.0
19
+ Requires-Dist: watchdog<7.0.0,>=6.0.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Tina4 Python — This is not a framework
23
+
24
+ Laravel joy. Python speed. 10x less code.
25
+
26
+ ## Quickstart
27
+
28
+ ```bash
29
+ pip install tina4-python
30
+ tina4 init my_project
31
+ cd my_project
32
+ python app.py
33
+ ```
34
+
35
+ You've just built your first Tina4 app — zero configuration, zero classes, zero boilerplate!
36
+
37
+ > **Prefer uv?** Replace `pip install tina4-python` with `uv add tina4-python`, then use `uv run tina4 start` to launch the dev server.
38
+
39
+ ## Features
40
+
41
+ - **ASGI compliant** — works with any ASGI server (uvicorn, hypercorn, daphne)
42
+ - **Full async** — every route handler is `async` by default
43
+ - **Routing** — decorator-based with path parameters, type hints, and auto-discovery
44
+ - **Twig/Jinja2 templates** — with inheritance, partials, custom filters, and globals
45
+ - **ORM** — define models with typed fields, save/load/select/delete with one line
46
+ - **Database** — SQLite, PostgreSQL, MySQL, MariaDB, MSSQL, Firebird
47
+ - **Migrations** — versioned SQL files, CLI scaffolding
48
+ - **Sessions** — file, Redis, Valkey, or MongoDB backends
49
+ - **JWT authentication** — auto-generated RSA keys, bearer tokens, form tokens
50
+ - **Swagger/OpenAPI** — auto-generated docs at `/swagger`
51
+ - **CRUD scaffolding** — instant admin UI with one line of code
52
+ - **Middleware** — before/after hooks per route or globally
53
+ - **Queues** — background processing with litequeue, RabbitMQ, Kafka, or MongoDB
54
+ - **WebSockets** — built-in support via `simple-websocket`
55
+ - **WSDL/SOAP** — auto-generated WSDL from Python classes
56
+ - **REST client** — built-in `Api` class for external HTTP calls
57
+ - **SCSS compilation** — auto-compiled to CSS on save
58
+ - **Live reload** — browser auto-refreshes during development
59
+ - **Inline testing** — decorator-based test cases with `@tests`
60
+ - **Localization** — i18n via gettext (English, French, Afrikaans)
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pip install tina4-python
66
+ ```
67
+
68
+ Or with [uv](https://docs.astral.sh/uv/) (recommended for dependency management):
69
+
70
+ ```bash
71
+ uv add tina4-python
72
+ ```
73
+
74
+ ## Routing
75
+
76
+ Routes live in `src/routes/` and are auto-discovered on startup.
77
+
78
+ ```python
79
+ # src/routes/hello.py
80
+ from tina4_python import get
81
+
82
+ @get("/hello")
83
+ async def get_hello(request, response):
84
+ return response("Hello, Tina4 Python!")
85
+
86
+ @get("/hello/{name}")
87
+ async def get_hello_name(name, request, response):
88
+ return response(f"Hello, {name}")
89
+
90
+ @get("/hello/json")
91
+ async def get_hello_json(request, response):
92
+ return response([{"brand": "BMW"}, {"brand": "Toyota"}])
93
+
94
+ @get("/hello/template")
95
+ async def get_hello_template(request, response):
96
+ return response.render("index.twig", {"data": request.params})
97
+
98
+ @get("/hello/redirect")
99
+ async def get_hello_redirect(request, response):
100
+ return response.redirect("/hello/world")
101
+ ```
102
+
103
+ ## ORM
104
+
105
+ ```python
106
+ # src/orm/User.py
107
+ from tina4_python import ORM, IntegerField, StringField
108
+
109
+ class User(ORM):
110
+ id = IntegerField(primary_key=True, auto_increment=True)
111
+ name = StringField()
112
+ email = StringField()
113
+
114
+ User({"name": "Alice", "email": "alice@example.com"}).save()
115
+ ```
116
+
117
+ ## Database
118
+
119
+ ```python
120
+ from tina4_python.Database import Database
121
+
122
+ db = Database("sqlite3:app.db")
123
+ result = db.fetch("SELECT * FROM users WHERE age > ?", [18])
124
+ ```
125
+
126
+ Works with SQLite, PostgreSQL, MySQL, MariaDB, MSSQL, and Firebird.
127
+
128
+ ## Migrations
129
+
130
+ ```bash
131
+ tina4 migrate:create "create users table"
132
+ # Edit the generated SQL file in migrations/
133
+ tina4 migrate
134
+ ```
135
+
136
+ ## Sessions
137
+
138
+ Built-in session management with pluggable backends:
139
+
140
+ | Handler | Backend | Package |
141
+ |---------|---------|---------|
142
+ | `SessionFileHandler` (default) | File system | — |
143
+ | `SessionRedisHandler` | Redis | `redis` |
144
+ | `SessionValkeyHandler` | Valkey | `valkey` |
145
+ | `SessionMongoHandler` | MongoDB | `pymongo` |
146
+
147
+ ```python
148
+ request.session.set("name", "Joe")
149
+ name = request.session.get("name")
150
+ ```
151
+
152
+ ## Queues
153
+
154
+ Background processing with litequeue (default), RabbitMQ, Kafka, or MongoDB.
155
+
156
+ ```python
157
+ from tina4_python.Queue import Queue, Producer, Consumer
158
+
159
+ Producer(Queue(topic="emails")).produce({"to": "alice@example.com"})
160
+
161
+ for msg in Consumer(Queue(topic="emails")).messages():
162
+ print(msg.data)
163
+ ```
164
+
165
+ ## Middleware
166
+
167
+ ```python
168
+ from tina4_python.Router import get, middleware
169
+
170
+ class AuthCheck:
171
+ @staticmethod
172
+ def before_auth(request, response):
173
+ if "authorization" not in request.headers:
174
+ return request, response("Unauthorized", 401)
175
+ return request, response
176
+
177
+ @middleware(AuthCheck)
178
+ @get("/protected")
179
+ async def protected(request, response):
180
+ return response({"secret": True})
181
+ ```
182
+
183
+ ## Swagger / OpenAPI
184
+
185
+ Auto-generated at `/swagger`. Add metadata with decorators:
186
+
187
+ ```python
188
+ from tina4_python import description, tags
189
+
190
+ @get("/users")
191
+ @description("Get all users")
192
+ @tags(["users"])
193
+ async def users(request, response):
194
+ return response(User().select("*"))
195
+ ```
196
+
197
+ ## REST Client
198
+
199
+ ```python
200
+ from tina4_python import Api
201
+
202
+ api = Api("https://api.example.com", auth_header="Bearer xyz")
203
+ result = api.get("/users/42")
204
+ ```
205
+
206
+ ## WSDL / SOAP
207
+
208
+ ```python
209
+ from tina4_python.WSDL import WSDL, wsdl_operation
210
+
211
+ class Calculator(WSDL):
212
+ SERVICE_URL = "http://localhost:7145/calculator"
213
+
214
+ def Add(self, a: int, b: int):
215
+ return {"Result": a + b}
216
+ ```
217
+
218
+ ## Testing
219
+
220
+ ```python
221
+ from tina4_python import tests
222
+
223
+ @tests(
224
+ assert_equal((7, 7), 1),
225
+ assert_raises(ZeroDivisionError, (5, 0)),
226
+ )
227
+ def divide(a: int, b: int) -> float:
228
+ if b == 0:
229
+ raise ZeroDivisionError("division by zero")
230
+ return a / b
231
+ ```
232
+
233
+ Run with `tina4 test` or `uv run pytest --verbose`.
234
+
235
+ ## Environment
236
+
237
+ Key `.env` settings:
238
+
239
+ ```bash
240
+ SECRET=your-jwt-secret
241
+ API_KEY=your-api-key
242
+ DATABASE_NAME=sqlite3:app.db
243
+ TINA4_DEBUG_LEVEL=ALL
244
+ TINA4_LANGUAGE=en
245
+ TINA4_SESSION_HANDLER=SessionFileHandler
246
+ SWAGGER_TITLE=My API
247
+ ```
248
+
249
+ ## Further Documentation
250
+
251
+ https://tina4.com/python
252
+
253
+ ## Community
254
+
255
+ - GitHub: https://github.com/tina4stack/tina4-python
256
+
257
+ ## License
258
+
259
+ MIT (c) 2007-2025 Tina4 Stack
260
+ https://opensource.org/licenses/MIT
261
+
262
+ ---
263
+
264
+ **Tina4** — The framework that keeps out of the way of your coding.
@@ -0,0 +1,243 @@
1
+ # Tina4 Python — This is not a framework
2
+
3
+ Laravel joy. Python speed. 10x less code.
4
+
5
+ ## Quickstart
6
+
7
+ ```bash
8
+ pip install tina4-python
9
+ tina4 init my_project
10
+ cd my_project
11
+ python app.py
12
+ ```
13
+
14
+ You've just built your first Tina4 app — zero configuration, zero classes, zero boilerplate!
15
+
16
+ > **Prefer uv?** Replace `pip install tina4-python` with `uv add tina4-python`, then use `uv run tina4 start` to launch the dev server.
17
+
18
+ ## Features
19
+
20
+ - **ASGI compliant** — works with any ASGI server (uvicorn, hypercorn, daphne)
21
+ - **Full async** — every route handler is `async` by default
22
+ - **Routing** — decorator-based with path parameters, type hints, and auto-discovery
23
+ - **Twig/Jinja2 templates** — with inheritance, partials, custom filters, and globals
24
+ - **ORM** — define models with typed fields, save/load/select/delete with one line
25
+ - **Database** — SQLite, PostgreSQL, MySQL, MariaDB, MSSQL, Firebird
26
+ - **Migrations** — versioned SQL files, CLI scaffolding
27
+ - **Sessions** — file, Redis, Valkey, or MongoDB backends
28
+ - **JWT authentication** — auto-generated RSA keys, bearer tokens, form tokens
29
+ - **Swagger/OpenAPI** — auto-generated docs at `/swagger`
30
+ - **CRUD scaffolding** — instant admin UI with one line of code
31
+ - **Middleware** — before/after hooks per route or globally
32
+ - **Queues** — background processing with litequeue, RabbitMQ, Kafka, or MongoDB
33
+ - **WebSockets** — built-in support via `simple-websocket`
34
+ - **WSDL/SOAP** — auto-generated WSDL from Python classes
35
+ - **REST client** — built-in `Api` class for external HTTP calls
36
+ - **SCSS compilation** — auto-compiled to CSS on save
37
+ - **Live reload** — browser auto-refreshes during development
38
+ - **Inline testing** — decorator-based test cases with `@tests`
39
+ - **Localization** — i18n via gettext (English, French, Afrikaans)
40
+
41
+ ## Install
42
+
43
+ ```bash
44
+ pip install tina4-python
45
+ ```
46
+
47
+ Or with [uv](https://docs.astral.sh/uv/) (recommended for dependency management):
48
+
49
+ ```bash
50
+ uv add tina4-python
51
+ ```
52
+
53
+ ## Routing
54
+
55
+ Routes live in `src/routes/` and are auto-discovered on startup.
56
+
57
+ ```python
58
+ # src/routes/hello.py
59
+ from tina4_python import get
60
+
61
+ @get("/hello")
62
+ async def get_hello(request, response):
63
+ return response("Hello, Tina4 Python!")
64
+
65
+ @get("/hello/{name}")
66
+ async def get_hello_name(name, request, response):
67
+ return response(f"Hello, {name}")
68
+
69
+ @get("/hello/json")
70
+ async def get_hello_json(request, response):
71
+ return response([{"brand": "BMW"}, {"brand": "Toyota"}])
72
+
73
+ @get("/hello/template")
74
+ async def get_hello_template(request, response):
75
+ return response.render("index.twig", {"data": request.params})
76
+
77
+ @get("/hello/redirect")
78
+ async def get_hello_redirect(request, response):
79
+ return response.redirect("/hello/world")
80
+ ```
81
+
82
+ ## ORM
83
+
84
+ ```python
85
+ # src/orm/User.py
86
+ from tina4_python import ORM, IntegerField, StringField
87
+
88
+ class User(ORM):
89
+ id = IntegerField(primary_key=True, auto_increment=True)
90
+ name = StringField()
91
+ email = StringField()
92
+
93
+ User({"name": "Alice", "email": "alice@example.com"}).save()
94
+ ```
95
+
96
+ ## Database
97
+
98
+ ```python
99
+ from tina4_python.Database import Database
100
+
101
+ db = Database("sqlite3:app.db")
102
+ result = db.fetch("SELECT * FROM users WHERE age > ?", [18])
103
+ ```
104
+
105
+ Works with SQLite, PostgreSQL, MySQL, MariaDB, MSSQL, and Firebird.
106
+
107
+ ## Migrations
108
+
109
+ ```bash
110
+ tina4 migrate:create "create users table"
111
+ # Edit the generated SQL file in migrations/
112
+ tina4 migrate
113
+ ```
114
+
115
+ ## Sessions
116
+
117
+ Built-in session management with pluggable backends:
118
+
119
+ | Handler | Backend | Package |
120
+ |---------|---------|---------|
121
+ | `SessionFileHandler` (default) | File system | — |
122
+ | `SessionRedisHandler` | Redis | `redis` |
123
+ | `SessionValkeyHandler` | Valkey | `valkey` |
124
+ | `SessionMongoHandler` | MongoDB | `pymongo` |
125
+
126
+ ```python
127
+ request.session.set("name", "Joe")
128
+ name = request.session.get("name")
129
+ ```
130
+
131
+ ## Queues
132
+
133
+ Background processing with litequeue (default), RabbitMQ, Kafka, or MongoDB.
134
+
135
+ ```python
136
+ from tina4_python.Queue import Queue, Producer, Consumer
137
+
138
+ Producer(Queue(topic="emails")).produce({"to": "alice@example.com"})
139
+
140
+ for msg in Consumer(Queue(topic="emails")).messages():
141
+ print(msg.data)
142
+ ```
143
+
144
+ ## Middleware
145
+
146
+ ```python
147
+ from tina4_python.Router import get, middleware
148
+
149
+ class AuthCheck:
150
+ @staticmethod
151
+ def before_auth(request, response):
152
+ if "authorization" not in request.headers:
153
+ return request, response("Unauthorized", 401)
154
+ return request, response
155
+
156
+ @middleware(AuthCheck)
157
+ @get("/protected")
158
+ async def protected(request, response):
159
+ return response({"secret": True})
160
+ ```
161
+
162
+ ## Swagger / OpenAPI
163
+
164
+ Auto-generated at `/swagger`. Add metadata with decorators:
165
+
166
+ ```python
167
+ from tina4_python import description, tags
168
+
169
+ @get("/users")
170
+ @description("Get all users")
171
+ @tags(["users"])
172
+ async def users(request, response):
173
+ return response(User().select("*"))
174
+ ```
175
+
176
+ ## REST Client
177
+
178
+ ```python
179
+ from tina4_python import Api
180
+
181
+ api = Api("https://api.example.com", auth_header="Bearer xyz")
182
+ result = api.get("/users/42")
183
+ ```
184
+
185
+ ## WSDL / SOAP
186
+
187
+ ```python
188
+ from tina4_python.WSDL import WSDL, wsdl_operation
189
+
190
+ class Calculator(WSDL):
191
+ SERVICE_URL = "http://localhost:7145/calculator"
192
+
193
+ def Add(self, a: int, b: int):
194
+ return {"Result": a + b}
195
+ ```
196
+
197
+ ## Testing
198
+
199
+ ```python
200
+ from tina4_python import tests
201
+
202
+ @tests(
203
+ assert_equal((7, 7), 1),
204
+ assert_raises(ZeroDivisionError, (5, 0)),
205
+ )
206
+ def divide(a: int, b: int) -> float:
207
+ if b == 0:
208
+ raise ZeroDivisionError("division by zero")
209
+ return a / b
210
+ ```
211
+
212
+ Run with `tina4 test` or `uv run pytest --verbose`.
213
+
214
+ ## Environment
215
+
216
+ Key `.env` settings:
217
+
218
+ ```bash
219
+ SECRET=your-jwt-secret
220
+ API_KEY=your-api-key
221
+ DATABASE_NAME=sqlite3:app.db
222
+ TINA4_DEBUG_LEVEL=ALL
223
+ TINA4_LANGUAGE=en
224
+ TINA4_SESSION_HANDLER=SessionFileHandler
225
+ SWAGGER_TITLE=My API
226
+ ```
227
+
228
+ ## Further Documentation
229
+
230
+ https://tina4.com/python
231
+
232
+ ## Community
233
+
234
+ - GitHub: https://github.com/tina4stack/tina4-python
235
+
236
+ ## License
237
+
238
+ MIT (c) 2007-2025 Tina4 Stack
239
+ https://opensource.org/licenses/MIT
240
+
241
+ ---
242
+
243
+ **Tina4** — The framework that keeps out of the way of your coding.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tina4-python"
3
- version = "0.2.188"
3
+ version = "0.2.190"
4
4
  description = "Tina4Python - This is not another framework for Python"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam",email = "andrevanzuydam@gmail.com"}
@@ -181,7 +181,8 @@ class Auth:
181
181
  self.secret = os.environ.get("SECRET", "{self.secret}")
182
182
  if self.secret == "{self.secret}":
183
183
  from tina4_python.Debug import Debug
184
- Debug.warning("No SECRET env var set - using default secret. Set SECRET in your .env for production.")
184
+ from tina4_python import Messages
185
+ Debug.warning(Messages.MSG_AUTH_NO_SECRET)
185
186
  self.private_key = os.path.join(root_path, "secrets", "private.key")
186
187
  self.public_key = os.path.join(root_path, "secrets", "public.key")
187
188
  self.self_signed = os.path.join(root_path, "secrets", "domain.cert")