xitzin 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- xitzin/__init__.py +9 -1
- xitzin/application.py +278 -9
- xitzin/auth.py +28 -2
- xitzin/cgi.py +21 -10
- xitzin/exceptions.py +18 -0
- xitzin/middleware.py +171 -6
- xitzin/requests.py +103 -0
- xitzin/responses.py +2 -3
- xitzin/routing.py +165 -0
- xitzin/scgi.py +447 -0
- xitzin/tasks.py +176 -0
- xitzin/testing.py +111 -1
- {xitzin-0.2.0.dist-info → xitzin-0.4.0.dist-info}/METADATA +7 -4
- xitzin-0.4.0.dist-info/RECORD +18 -0
- {xitzin-0.2.0.dist-info → xitzin-0.4.0.dist-info}/WHEEL +2 -2
- xitzin-0.2.0.dist-info/RECORD +0 -16
xitzin/testing.py
CHANGED
|
@@ -13,6 +13,7 @@ from typing import TYPE_CHECKING, Generator
|
|
|
13
13
|
from urllib.parse import quote_plus
|
|
14
14
|
|
|
15
15
|
from nauyaca.protocol.request import GeminiRequest
|
|
16
|
+
from nauyaca.protocol.request import TitanRequest as NauyacaTitanRequest
|
|
16
17
|
from nauyaca.protocol.response import GeminiResponse
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
@@ -153,10 +154,15 @@ class TestClient:
|
|
|
153
154
|
# Handle request through the app
|
|
154
155
|
response = self._handle_sync(request)
|
|
155
156
|
|
|
157
|
+
# Convert bytes body to str for TestResponse
|
|
158
|
+
body = response.body
|
|
159
|
+
if isinstance(body, bytes):
|
|
160
|
+
body = body.decode("utf-8")
|
|
161
|
+
|
|
156
162
|
return TestResponse(
|
|
157
163
|
status=response.status,
|
|
158
164
|
meta=response.meta,
|
|
159
|
-
body=
|
|
165
|
+
body=body,
|
|
160
166
|
)
|
|
161
167
|
|
|
162
168
|
def get_input(
|
|
@@ -210,6 +216,100 @@ class TestClient:
|
|
|
210
216
|
new_client._default_fingerprint = fingerprint
|
|
211
217
|
return new_client
|
|
212
218
|
|
|
219
|
+
def upload(
|
|
220
|
+
self,
|
|
221
|
+
path: str,
|
|
222
|
+
content: bytes | str,
|
|
223
|
+
*,
|
|
224
|
+
mime_type: str = "text/gemini",
|
|
225
|
+
token: str | None = None,
|
|
226
|
+
cert_fingerprint: str | None = None,
|
|
227
|
+
) -> TestResponse:
|
|
228
|
+
"""Make a Titan upload request.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
path: The request path (e.g., "/upload/file.gmi").
|
|
232
|
+
content: Content to upload (str will be UTF-8 encoded).
|
|
233
|
+
mime_type: Content MIME type (default: text/gemini).
|
|
234
|
+
token: Authentication token (if required by route).
|
|
235
|
+
cert_fingerprint: Mock client certificate fingerprint.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
TestResponse with status, meta, and body.
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
response = client.upload(
|
|
242
|
+
"/files/test.gmi",
|
|
243
|
+
"# Hello World",
|
|
244
|
+
mime_type="text/gemini",
|
|
245
|
+
token="secret123"
|
|
246
|
+
)
|
|
247
|
+
assert response.is_success
|
|
248
|
+
"""
|
|
249
|
+
# Convert str to bytes
|
|
250
|
+
if isinstance(content, str):
|
|
251
|
+
content_bytes = content.encode("utf-8")
|
|
252
|
+
else:
|
|
253
|
+
content_bytes = content
|
|
254
|
+
|
|
255
|
+
# Build Titan URL
|
|
256
|
+
size = len(content_bytes)
|
|
257
|
+
url = f"titan://testserver{path};size={size};mime={mime_type}"
|
|
258
|
+
if token:
|
|
259
|
+
url += f";token={token}"
|
|
260
|
+
|
|
261
|
+
# Create TitanRequest
|
|
262
|
+
request = NauyacaTitanRequest.from_line(url)
|
|
263
|
+
request.content = content_bytes
|
|
264
|
+
|
|
265
|
+
# Set certificate info
|
|
266
|
+
fingerprint = cert_fingerprint or self._default_fingerprint
|
|
267
|
+
if fingerprint:
|
|
268
|
+
request.client_cert_fingerprint = fingerprint
|
|
269
|
+
|
|
270
|
+
# Handle through app
|
|
271
|
+
response = self._handle_titan_sync(request)
|
|
272
|
+
|
|
273
|
+
# Convert bytes body to str for TestResponse
|
|
274
|
+
body = response.body
|
|
275
|
+
if isinstance(body, bytes):
|
|
276
|
+
body = body.decode("utf-8")
|
|
277
|
+
|
|
278
|
+
return TestResponse(
|
|
279
|
+
status=response.status,
|
|
280
|
+
meta=response.meta,
|
|
281
|
+
body=body,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
def delete(
|
|
285
|
+
self,
|
|
286
|
+
path: str,
|
|
287
|
+
*,
|
|
288
|
+
token: str | None = None,
|
|
289
|
+
cert_fingerprint: str | None = None,
|
|
290
|
+
) -> TestResponse:
|
|
291
|
+
"""Make a Titan delete request (zero-byte upload).
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
path: The request path to delete.
|
|
295
|
+
token: Authentication token (if required by route).
|
|
296
|
+
cert_fingerprint: Mock client certificate fingerprint.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
TestResponse with status, meta, and body.
|
|
300
|
+
|
|
301
|
+
Example:
|
|
302
|
+
response = client.delete("/files/old.gmi", token="secret123")
|
|
303
|
+
assert response.is_success
|
|
304
|
+
"""
|
|
305
|
+
return self.upload(
|
|
306
|
+
path,
|
|
307
|
+
b"",
|
|
308
|
+
mime_type="text/gemini",
|
|
309
|
+
token=token,
|
|
310
|
+
cert_fingerprint=cert_fingerprint,
|
|
311
|
+
)
|
|
312
|
+
|
|
213
313
|
def _handle_sync(self, request: GeminiRequest) -> GeminiResponse:
|
|
214
314
|
"""Handle a request synchronously."""
|
|
215
315
|
try:
|
|
@@ -220,6 +320,16 @@ class TestClient:
|
|
|
220
320
|
|
|
221
321
|
return loop.run_until_complete(self._app._handle_request(request))
|
|
222
322
|
|
|
323
|
+
def _handle_titan_sync(self, request: NauyacaTitanRequest) -> GeminiResponse:
|
|
324
|
+
"""Handle a Titan request synchronously."""
|
|
325
|
+
try:
|
|
326
|
+
loop = asyncio.get_event_loop()
|
|
327
|
+
except RuntimeError:
|
|
328
|
+
loop = asyncio.new_event_loop()
|
|
329
|
+
asyncio.set_event_loop(loop)
|
|
330
|
+
|
|
331
|
+
return loop.run_until_complete(self._app._handle_titan_request(request))
|
|
332
|
+
|
|
223
333
|
|
|
224
334
|
@contextmanager
|
|
225
335
|
def test_app(app: "Xitzin") -> Generator[TestClient, None, None]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: xitzin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A Gemini Application Framework
|
|
5
5
|
Keywords: gemini,protocol,framework,async,geminispace
|
|
6
6
|
Author: Alan Velasco
|
|
@@ -20,15 +20,18 @@ Classifier: Typing :: Typed
|
|
|
20
20
|
Requires-Dist: jinja2>=3.1.0
|
|
21
21
|
Requires-Dist: nauyaca>=0.3.2
|
|
22
22
|
Requires-Dist: rich>=14.2.0
|
|
23
|
+
Requires-Dist: structlog>=25.5.0
|
|
23
24
|
Requires-Dist: typing-extensions>=4.15.0
|
|
24
25
|
Requires-Dist: sqlmodel>=0.0.22 ; extra == 'sqlmodel'
|
|
26
|
+
Requires-Dist: croniter>=1.0.0 ; extra == 'tasks'
|
|
25
27
|
Requires-Python: >=3.10
|
|
26
|
-
Project-URL: Changelog, https://xitzin.readthedocs.io/changelog/
|
|
27
|
-
Project-URL: Documentation, https://xitzin.readthedocs.io
|
|
28
28
|
Project-URL: Homepage, https://github.com/alanbato/xitzin
|
|
29
|
-
Project-URL:
|
|
29
|
+
Project-URL: Documentation, https://xitzin.readthedocs.io
|
|
30
30
|
Project-URL: Repository, https://github.com/alanbato/xitzin.git
|
|
31
|
+
Project-URL: Issues, https://github.com/alanbato/xitzin/issues
|
|
32
|
+
Project-URL: Changelog, https://xitzin.readthedocs.io/changelog/
|
|
31
33
|
Provides-Extra: sqlmodel
|
|
34
|
+
Provides-Extra: tasks
|
|
32
35
|
Description-Content-Type: text/markdown
|
|
33
36
|
|
|
34
37
|
# Xitzin
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
xitzin/__init__.py,sha256=GMfAmzBJ6TMZJWfF38r1k0y-vswOdLw9xfWpPfYMcqc,1851
|
|
2
|
+
xitzin/application.py,sha256=axL5G4lRz0KTNLsNQkCGxWD0MGDpIH6Xe9OKkxJlJw8,27851
|
|
3
|
+
xitzin/auth.py,sha256=hHhrP_fYToY6J6CCU8vgAsWbYn6K16LUxkeDMzZTF2Q,5304
|
|
4
|
+
xitzin/cgi.py,sha256=nggSuQ0gXIKdBKWHqH_CNZ6UlVGaB3VAOu3ZRPc43ek,18046
|
|
5
|
+
xitzin/exceptions.py,sha256=82z-CjyC0FwFbo9hGTjjmurlL_Vd4rTVdgkmQoFLXT0,3883
|
|
6
|
+
xitzin/middleware.py,sha256=dZhrbOJPCrrbMiwrbRzxElsC7UDrWc7lvnQ5c0xBHts,12542
|
|
7
|
+
xitzin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
xitzin/requests.py,sha256=1mbm5F8oywLPNpSdh2inn7K1IEeI3H5meOwj8jldi1Q,7783
|
|
9
|
+
xitzin/responses.py,sha256=QVH4B-qVHP7hxi0hIrOJhdCiHDf4PDCpSZ1fAD8iNkk,6525
|
|
10
|
+
xitzin/routing.py,sha256=8Kl3FV6EWZ6nW0bDTqJ-qysNyJ1iSRtCKicCAai8Lmk,18093
|
|
11
|
+
xitzin/scgi.py,sha256=t-ixEwrQR-bIRj56DkzBsvGyN75E5R0gasIjMfq5LFY,13665
|
|
12
|
+
xitzin/sqlmodel.py,sha256=lZvDzYAgnG8S2K-EYnx6PkO7D68pjM06uQLSKUZ9yY4,4500
|
|
13
|
+
xitzin/tasks.py,sha256=_smEXy-THge8wmQqWDtX3iUmAmoHniw_qqZBlKjCdqA,4597
|
|
14
|
+
xitzin/templating.py,sha256=spjxb05wgwKA4txOMbWJuwEVMaoLovo13_ukbXAcBDY,6040
|
|
15
|
+
xitzin/testing.py,sha256=wMiToopD2TAqTsreVqOrIUNCayORNQQennxR_GwMBSY,10980
|
|
16
|
+
xitzin-0.4.0.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
|
|
17
|
+
xitzin-0.4.0.dist-info/METADATA,sha256=Zv-Rde7LWIcf_FQfclXu77BzGipWf6_n0fOXVzkvk0A,3456
|
|
18
|
+
xitzin-0.4.0.dist-info/RECORD,,
|
xitzin-0.2.0.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
xitzin/__init__.py,sha256=9zCk_h4rI-QGLE3q95NSgPha-dtvSOEdwiZuTQmbBR4,1637
|
|
2
|
-
xitzin/application.py,sha256=GWcL-u1C6xiUwYwMxg60kQxprJ_KaLcJFPcIBf8xCzU,17909
|
|
3
|
-
xitzin/auth.py,sha256=KT1WprT4qF1u03T8lAGO_UzQBLQcg-OegIFubay7VlA,4511
|
|
4
|
-
xitzin/cgi.py,sha256=nmKaeLwYfk3esRnxQnn1Rx-6EHKq2zL-Xvpw5hdI-rk,17627
|
|
5
|
-
xitzin/exceptions.py,sha256=JdxAXHtnXMf6joXeFQMfxlcdbv_R5odb-qYPMbgRTZY,3382
|
|
6
|
-
xitzin/middleware.py,sha256=z2QJJjMEinz5e4NeZvjsCeyMLt8ZD2a3Ox2V2vuTnk8,6748
|
|
7
|
-
xitzin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
xitzin/requests.py,sha256=EeDqh0roz7eTItqarEDj53iFxMZlVsEIcimybKdqAHI,4612
|
|
9
|
-
xitzin/responses.py,sha256=N4HeP9Yy2PIHY0zsGa2pj8xvX2OFHAjtqR5nogNh8UQ,6545
|
|
10
|
-
xitzin/routing.py,sha256=SiT9J617GfQR_rPDD3Ivw0_N4CTh8-Jb_AZjWJnT5sw,12409
|
|
11
|
-
xitzin/sqlmodel.py,sha256=lZvDzYAgnG8S2K-EYnx6PkO7D68pjM06uQLSKUZ9yY4,4500
|
|
12
|
-
xitzin/templating.py,sha256=spjxb05wgwKA4txOMbWJuwEVMaoLovo13_ukbXAcBDY,6040
|
|
13
|
-
xitzin/testing.py,sha256=JO41TeIJeb1CqHVqBOjCVAvv9BOlvDJYzAeG83ZofdE,7572
|
|
14
|
-
xitzin-0.2.0.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
|
|
15
|
-
xitzin-0.2.0.dist-info/METADATA,sha256=Jvf0vYB5goyuimrOa4321LhntdLNJKxIJCUpuIyTmbQ,3351
|
|
16
|
-
xitzin-0.2.0.dist-info/RECORD,,
|