xitzin 0.1.2__tar.gz → 0.2.0__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.
- {xitzin-0.1.2 → xitzin-0.2.0}/PKG-INFO +3 -1
- {xitzin-0.1.2 → xitzin-0.2.0}/pyproject.toml +7 -1
- xitzin-0.2.0/src/xitzin/sqlmodel.py +172 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/README.md +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/__init__.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/application.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/auth.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/cgi.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/exceptions.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/middleware.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/py.typed +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/requests.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/responses.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/routing.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/templating.py +0 -0
- {xitzin-0.1.2 → xitzin-0.2.0}/src/xitzin/testing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: xitzin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A Gemini Application Framework
|
|
5
5
|
Keywords: gemini,protocol,framework,async,geminispace
|
|
6
6
|
Author: Alan Velasco
|
|
@@ -21,12 +21,14 @@ Requires-Dist: jinja2>=3.1.0
|
|
|
21
21
|
Requires-Dist: nauyaca>=0.3.2
|
|
22
22
|
Requires-Dist: rich>=14.2.0
|
|
23
23
|
Requires-Dist: typing-extensions>=4.15.0
|
|
24
|
+
Requires-Dist: sqlmodel>=0.0.22 ; extra == 'sqlmodel'
|
|
24
25
|
Requires-Python: >=3.10
|
|
25
26
|
Project-URL: Changelog, https://xitzin.readthedocs.io/changelog/
|
|
26
27
|
Project-URL: Documentation, https://xitzin.readthedocs.io
|
|
27
28
|
Project-URL: Homepage, https://github.com/alanbato/xitzin
|
|
28
29
|
Project-URL: Issues, https://github.com/alanbato/xitzin/issues
|
|
29
30
|
Project-URL: Repository, https://github.com/alanbato/xitzin.git
|
|
31
|
+
Provides-Extra: sqlmodel
|
|
30
32
|
Description-Content-Type: text/markdown
|
|
31
33
|
|
|
32
34
|
# Xitzin
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "xitzin"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A Gemini Application Framework"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -29,6 +29,11 @@ dependencies = [
|
|
|
29
29
|
"typing-extensions>=4.15.0",
|
|
30
30
|
]
|
|
31
31
|
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
sqlmodel = [
|
|
34
|
+
"sqlmodel>=0.0.22",
|
|
35
|
+
]
|
|
36
|
+
|
|
32
37
|
[project.urls]
|
|
33
38
|
Homepage = "https://github.com/alanbato/xitzin"
|
|
34
39
|
Documentation = "https://xitzin.readthedocs.io"
|
|
@@ -47,6 +52,7 @@ dev = [
|
|
|
47
52
|
"pytest-asyncio>=0.24.0",
|
|
48
53
|
"pytest-cov>=7.0.0",
|
|
49
54
|
"ruff>=0.14.10",
|
|
55
|
+
"sqlmodel>=0.0.31",
|
|
50
56
|
"ty>=0.0.8",
|
|
51
57
|
]
|
|
52
58
|
docs = [
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""SQLModel integration for Xitzin.
|
|
2
|
+
|
|
3
|
+
This module provides database session management through middleware
|
|
4
|
+
and helper functions. Requires the 'sqlmodel' optional dependency.
|
|
5
|
+
|
|
6
|
+
Install with: pip install xitzin[sqlmodel]
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
from sqlmodel import Field, select
|
|
10
|
+
from xitzin import Xitzin, Request
|
|
11
|
+
from xitzin.sqlmodel import (
|
|
12
|
+
SQLModel,
|
|
13
|
+
create_engine,
|
|
14
|
+
SessionMiddleware,
|
|
15
|
+
get_session,
|
|
16
|
+
init_db,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Define models
|
|
20
|
+
class Entry(SQLModel, table=True):
|
|
21
|
+
id: int | None = Field(default=None, primary_key=True)
|
|
22
|
+
content: str
|
|
23
|
+
|
|
24
|
+
# Setup app
|
|
25
|
+
app = Xitzin()
|
|
26
|
+
engine = create_engine("sqlite:///./database.db")
|
|
27
|
+
|
|
28
|
+
# Initialize database and add middleware
|
|
29
|
+
init_db(app, engine)
|
|
30
|
+
app.middleware(SessionMiddleware(engine))
|
|
31
|
+
|
|
32
|
+
# Use in routes
|
|
33
|
+
@app.gemini("/entries")
|
|
34
|
+
def list_entries(request: Request):
|
|
35
|
+
session = get_session(request)
|
|
36
|
+
entries = session.exec(select(Entry)).all()
|
|
37
|
+
return render_entries(entries)
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
from typing import TYPE_CHECKING, Awaitable, Callable
|
|
43
|
+
|
|
44
|
+
from sqlalchemy import Engine
|
|
45
|
+
from sqlmodel import Session, SQLModel, create_engine
|
|
46
|
+
|
|
47
|
+
if TYPE_CHECKING:
|
|
48
|
+
from nauyaca.protocol.response import GeminiResponse
|
|
49
|
+
|
|
50
|
+
from xitzin.application import Xitzin
|
|
51
|
+
from xitzin.requests import Request
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"SQLModel",
|
|
55
|
+
"Session",
|
|
56
|
+
"create_engine",
|
|
57
|
+
"SessionMiddleware",
|
|
58
|
+
"get_session",
|
|
59
|
+
"init_db",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def SessionMiddleware(
|
|
64
|
+
engine: Engine,
|
|
65
|
+
*,
|
|
66
|
+
autoflush: bool = True,
|
|
67
|
+
) -> Callable[
|
|
68
|
+
["Request", Callable[["Request"], Awaitable["GeminiResponse"]]],
|
|
69
|
+
Awaitable["GeminiResponse"],
|
|
70
|
+
]:
|
|
71
|
+
"""Create a middleware that manages database sessions per request.
|
|
72
|
+
|
|
73
|
+
The session is stored in request.state.db and automatically committed
|
|
74
|
+
on success or rolled back on error.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
engine: SQLAlchemy engine instance.
|
|
78
|
+
autoflush: If True, flush before queries. Defaults to True.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Middleware function compatible with @app.middleware.
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
engine = create_engine("sqlite:///./database.db")
|
|
85
|
+
app.middleware(SessionMiddleware(engine))
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
async def middleware(
|
|
89
|
+
request: "Request",
|
|
90
|
+
call_next: Callable[["Request"], Awaitable["GeminiResponse"]],
|
|
91
|
+
) -> "GeminiResponse":
|
|
92
|
+
session = Session(engine, autoflush=autoflush)
|
|
93
|
+
request.state.db = session
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
response = await call_next(request)
|
|
97
|
+
session.commit()
|
|
98
|
+
return response
|
|
99
|
+
except Exception:
|
|
100
|
+
session.rollback()
|
|
101
|
+
raise
|
|
102
|
+
finally:
|
|
103
|
+
session.close()
|
|
104
|
+
|
|
105
|
+
return middleware
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_session(request: "Request") -> Session:
|
|
109
|
+
"""Get the database session from the current request.
|
|
110
|
+
|
|
111
|
+
This helper retrieves the session created by SessionMiddleware.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
request: The current Xitzin request object.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
SQLModel Session instance.
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
AttributeError: If SessionMiddleware is not configured.
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
@app.gemini("/users/{user_id}")
|
|
124
|
+
def get_user(request: Request, user_id: int):
|
|
125
|
+
session = get_session(request)
|
|
126
|
+
user = session.get(User, user_id)
|
|
127
|
+
return f"# {user.name}"
|
|
128
|
+
"""
|
|
129
|
+
if not hasattr(request.state, "db"):
|
|
130
|
+
raise AttributeError(
|
|
131
|
+
"No database session found. Did you add SessionMiddleware?"
|
|
132
|
+
)
|
|
133
|
+
return request.state.db
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def init_db(
|
|
137
|
+
app: "Xitzin",
|
|
138
|
+
engine: Engine,
|
|
139
|
+
*,
|
|
140
|
+
create_tables: bool = True,
|
|
141
|
+
drop_all: bool = False,
|
|
142
|
+
) -> None:
|
|
143
|
+
"""Initialize database with lifecycle hooks.
|
|
144
|
+
|
|
145
|
+
This helper registers startup/shutdown hooks to manage table creation
|
|
146
|
+
and engine cleanup.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
app: Xitzin application instance.
|
|
150
|
+
engine: SQLAlchemy engine instance.
|
|
151
|
+
create_tables: Create all tables on startup. Defaults to True.
|
|
152
|
+
drop_all: Drop all tables before creating. Defaults to False.
|
|
153
|
+
|
|
154
|
+
Warning:
|
|
155
|
+
Setting drop_all=True will DELETE ALL DATA on startup!
|
|
156
|
+
|
|
157
|
+
Example:
|
|
158
|
+
app = Xitzin()
|
|
159
|
+
engine = create_engine("sqlite:///./database.db")
|
|
160
|
+
init_db(app, engine)
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
@app.on_startup
|
|
164
|
+
def create_db_tables() -> None:
|
|
165
|
+
if drop_all:
|
|
166
|
+
SQLModel.metadata.drop_all(engine)
|
|
167
|
+
if create_tables:
|
|
168
|
+
SQLModel.metadata.create_all(engine)
|
|
169
|
+
|
|
170
|
+
@app.on_shutdown
|
|
171
|
+
def dispose_engine() -> None:
|
|
172
|
+
engine.dispose()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|