belgie 0.1.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.
belgie-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.3
2
+ Name: belgie
3
+ Version: 0.1.0
4
+ Summary: Modern authentication for FastAPI
5
+ Author: Matt LeMay
6
+ Author-email: Matt LeMay <mplemay@users.noreply.github.com>
7
+ Requires-Dist: belgie-proto
8
+ Requires-Dist: fastapi>=0.100
9
+ Requires-Dist: httpx>=0.24
10
+ Requires-Dist: pydantic>=2.0
11
+ Requires-Dist: pydantic-settings>=2.0
12
+ Requires-Dist: python-multipart>=0.0.20
13
+ Requires-Dist: belgie-alchemy ; extra == 'alchemy'
14
+ Requires-Dist: belgie-alchemy ; extra == 'all'
15
+ Requires-Dist: belgie-mcp ; extra == 'all'
16
+ Requires-Dist: belgie-oauth ; extra == 'all'
17
+ Requires-Dist: uvicorn[standard]>=0.38.0 ; extra == 'examples'
18
+ Requires-Dist: belgie-mcp ; extra == 'mcp'
19
+ Requires-Dist: belgie-oauth ; extra == 'oauth'
20
+ Requires-Python: >=3.12, <3.15
21
+ Provides-Extra: alchemy
22
+ Provides-Extra: all
23
+ Provides-Extra: examples
24
+ Provides-Extra: mcp
25
+ Provides-Extra: oauth
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Belgie
29
+
30
+ Self-hosted, type-safe authentication for FastAPI that makes Google OAuth and secure session cookies work with almost
31
+ zero glue code. Keep your data, skip per-user SaaS bills, and still get a polished developer experience.
32
+
33
+ ## Who this is for
34
+
35
+ - FastAPI teams that want Google sign-in and protected routes today, not after weeks of wiring.
36
+ - Product engineers who prefer first-class type hints and adapter-driven design over magic.
37
+ - Startups that would rather own their user data and avoid per-MAU pricing from hosted identity vendors.
38
+
39
+ ## What it solves
40
+
41
+ - End-to-end Google OAuth 2.0 flow with CSRF-safe state storage.
42
+ - Sliding-window, signed session cookies (no JWT juggling required).
43
+ - Drop-in FastAPI dependencies for `auth.user`, `auth.session`, and scoped access.
44
+ - A thin SQLAlchemy adapter that works with your existing models.
45
+ - Hooks so you can plug in logging, analytics, or audit trails without forking.
46
+
47
+ ## How it compares
48
+
49
+ - **fastapi-users**: feature-rich but now in maintenance mode and optimized for password-plus-OAuth flows. Belgie
50
+ focuses on OAuth + session UX, keeps the surface area small, and ships type-driven adapters out of the box.
51
+ - **Hosted identity (Auth0, Clerk, Supabase Auth)**: great UIs and more providers, but billed per Monthly Active User
52
+ and hosted off your stack. Belgie is MIT-licensed, runs in your app, and never charges per user.
53
+
54
+ ## Features at a glance
55
+
56
+ - Google OAuth provider with ready-made router (`/auth/signin/google`, `/auth/callback/google`, `/auth/signout`).
57
+ - Session manager with sliding expiry and secure cookie defaults (HttpOnly, SameSite, Secure).
58
+ - Scope-aware dependency for route protection (`Security(auth.user, scopes=[...])`).
59
+ - Modern Python (3.12+), full typing, and protocol-based models.
60
+ - Event hooks and utility helpers for custom workflows.
61
+
62
+ ## Installation
63
+
64
+ ```bash
65
+ pip install belgie
66
+ # or with uv
67
+ uv add belgie
68
+ ```
69
+
70
+ For SQLAlchemy adapter support:
71
+
72
+ ```bash
73
+ pip install belgie[alchemy]
74
+ # or with uv
75
+ uv add belgie[alchemy]
76
+ ```
77
+
78
+ Optional extras: `belgie[mcp]`, `belgie[oauth]`, or `belgie[all]`.
79
+
80
+ ## Quick start
81
+
82
+ ### 1) Define models
83
+
84
+ ```python
85
+ from datetime import UTC, datetime
86
+ from uuid import UUID, uuid4
87
+ from sqlalchemy import ForeignKey, String
88
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
89
+
90
+
91
+ class Base(DeclarativeBase):
92
+ pass
93
+
94
+
95
+ class User(Base):
96
+ __tablename__ = "users"
97
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
98
+ email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
99
+ name: Mapped[str | None] = mapped_column(String(255), nullable=True)
100
+ image: Mapped[str | None] = mapped_column(String(500), nullable=True)
101
+ email_verified: Mapped[bool] = mapped_column(default=False)
102
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
103
+ updated_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
104
+
105
+
106
+ class Account(Base):
107
+ __tablename__ = "accounts"
108
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
109
+ user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
110
+ provider: Mapped[str] = mapped_column(String(50))
111
+ provider_account_id: Mapped[str] = mapped_column(String(255))
112
+ access_token: Mapped[str | None] = mapped_column(String(1000), nullable=True)
113
+ refresh_token: Mapped[str | None] = mapped_column(String(1000), nullable=True)
114
+ expires_at: Mapped[datetime | None] = mapped_column(nullable=True)
115
+ scope: Mapped[str | None] = mapped_column(String(500), nullable=True)
116
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
117
+
118
+
119
+ class Session(Base):
120
+ __tablename__ = "sessions"
121
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
122
+ user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
123
+ expires_at: Mapped[datetime] = mapped_column(index=True)
124
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
125
+
126
+
127
+ class OAuthState(Base):
128
+ __tablename__ = "oauth_states"
129
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
130
+ state: Mapped[str] = mapped_column(String(255), unique=True, index=True)
131
+ expires_at: Mapped[datetime] = mapped_column(index=True)
132
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
133
+ ```
134
+
135
+ ### 2) Configure Belgie
136
+
137
+ ```python
138
+ from belgie.auth import Auth, AuthSettings, GoogleProviderSettings
139
+ from belgie_alchemy import AlchemyAdapter
140
+
141
+ settings = AuthSettings(
142
+ secret="your-secret-key",
143
+ base_url="http://localhost:8000",
144
+ )
145
+
146
+ adapter = AlchemyAdapter(
147
+ user=User,
148
+ account=Account,
149
+ session=Session,
150
+ oauth_state=OAuthState,
151
+ )
152
+
153
+ auth = Auth(
154
+ settings=settings,
155
+ adapter=adapter,
156
+ providers={
157
+ "google": GoogleProviderSettings(
158
+ client_id="your-google-client-id",
159
+ client_secret="your-google-client-secret",
160
+ redirect_uri="http://localhost:8000/auth/provider/google/callback",
161
+ scopes=["openid", "email", "profile"],
162
+ ),
163
+ },
164
+ )
165
+ ```
166
+
167
+ ### 3) Add routes to FastAPI
168
+
169
+ ```python
170
+ from fastapi import Depends, FastAPI, Security
171
+
172
+ app = FastAPI()
173
+ app.include_router(auth.router)
174
+
175
+
176
+ @app.get("/")
177
+ async def home():
178
+ return {"message": "Welcome! Visit /auth/provider/google/signin to sign in"}
179
+
180
+
181
+ @app.get("/protected")
182
+ async def protected(user: User = Depends(auth.user)):
183
+ return {"email": user.email}
184
+
185
+
186
+ @app.get("/profile")
187
+ async def profile(user: User = Security(auth.user, scopes=["profile"])):
188
+ return {"name": user.name, "email": user.email}
189
+ ```
190
+
191
+ Run it:
192
+
193
+ ```bash
194
+ uvicorn main:app --reload
195
+ ```
196
+
197
+ Visit `http://localhost:8000/auth/signin/google` to sign in.
198
+
199
+ ## Configuration shortcuts
200
+
201
+ - Environment variables: `BELGIE_SECRET`, `BELGIE_BASE_URL`, `BELGIE_GOOGLE_CLIENT_ID`, `BELGIE_GOOGLE_CLIENT_SECRET`,
202
+ `BELGIE_GOOGLE_REDIRECT_URI` (loaded automatically by `AuthSettings()`).
203
+ - Session tuning: `SessionSettings(cookie_name, max_age, update_age)` controls lifetime and sliding refresh.
204
+ - Cookie hardening: `CookieSettings(http_only, secure, same_site)` for production-ready defaults.
205
+
206
+ ## Router endpoints
207
+
208
+ - `GET /auth/signin/google` – start OAuth flow
209
+ - `GET /auth/callback/google` – handle Google callback
210
+ - `POST /auth/signout` – clear session cookie and invalidate server session
211
+
212
+ ## Limitations today
213
+
214
+ - Google is the only built-in provider; more providers and email/password are on the roadmap.
215
+ - You manage your own database migrations and deployment (by design—no third-party control plane).
216
+
217
+ ## Why teams pick Belgie
218
+
219
+ - Keep control of data and infra while getting a batteries-included OAuth flow.
220
+ - Minimal surface area: a single `Auth` instance exposes router + dependencies.
221
+ - Modern typing and clear protocols reduce integration mistakes and make refactors safer.
222
+ - MIT license, zero per-user costs.
223
+
224
+ ## Documentation and examples
225
+
226
+ - [docs/quickstart.md](docs/quickstart.md) for full walkthrough
227
+ - [examples/auth](examples/auth) for a runnable app
228
+
229
+ ## Contributing
230
+
231
+ MIT licensed. Issues and PRs welcome.
belgie-0.1.0/README.md ADDED
@@ -0,0 +1,204 @@
1
+ # Belgie
2
+
3
+ Self-hosted, type-safe authentication for FastAPI that makes Google OAuth and secure session cookies work with almost
4
+ zero glue code. Keep your data, skip per-user SaaS bills, and still get a polished developer experience.
5
+
6
+ ## Who this is for
7
+
8
+ - FastAPI teams that want Google sign-in and protected routes today, not after weeks of wiring.
9
+ - Product engineers who prefer first-class type hints and adapter-driven design over magic.
10
+ - Startups that would rather own their user data and avoid per-MAU pricing from hosted identity vendors.
11
+
12
+ ## What it solves
13
+
14
+ - End-to-end Google OAuth 2.0 flow with CSRF-safe state storage.
15
+ - Sliding-window, signed session cookies (no JWT juggling required).
16
+ - Drop-in FastAPI dependencies for `auth.user`, `auth.session`, and scoped access.
17
+ - A thin SQLAlchemy adapter that works with your existing models.
18
+ - Hooks so you can plug in logging, analytics, or audit trails without forking.
19
+
20
+ ## How it compares
21
+
22
+ - **fastapi-users**: feature-rich but now in maintenance mode and optimized for password-plus-OAuth flows. Belgie
23
+ focuses on OAuth + session UX, keeps the surface area small, and ships type-driven adapters out of the box.
24
+ - **Hosted identity (Auth0, Clerk, Supabase Auth)**: great UIs and more providers, but billed per Monthly Active User
25
+ and hosted off your stack. Belgie is MIT-licensed, runs in your app, and never charges per user.
26
+
27
+ ## Features at a glance
28
+
29
+ - Google OAuth provider with ready-made router (`/auth/signin/google`, `/auth/callback/google`, `/auth/signout`).
30
+ - Session manager with sliding expiry and secure cookie defaults (HttpOnly, SameSite, Secure).
31
+ - Scope-aware dependency for route protection (`Security(auth.user, scopes=[...])`).
32
+ - Modern Python (3.12+), full typing, and protocol-based models.
33
+ - Event hooks and utility helpers for custom workflows.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install belgie
39
+ # or with uv
40
+ uv add belgie
41
+ ```
42
+
43
+ For SQLAlchemy adapter support:
44
+
45
+ ```bash
46
+ pip install belgie[alchemy]
47
+ # or with uv
48
+ uv add belgie[alchemy]
49
+ ```
50
+
51
+ Optional extras: `belgie[mcp]`, `belgie[oauth]`, or `belgie[all]`.
52
+
53
+ ## Quick start
54
+
55
+ ### 1) Define models
56
+
57
+ ```python
58
+ from datetime import UTC, datetime
59
+ from uuid import UUID, uuid4
60
+ from sqlalchemy import ForeignKey, String
61
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
62
+
63
+
64
+ class Base(DeclarativeBase):
65
+ pass
66
+
67
+
68
+ class User(Base):
69
+ __tablename__ = "users"
70
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
71
+ email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
72
+ name: Mapped[str | None] = mapped_column(String(255), nullable=True)
73
+ image: Mapped[str | None] = mapped_column(String(500), nullable=True)
74
+ email_verified: Mapped[bool] = mapped_column(default=False)
75
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
76
+ updated_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
77
+
78
+
79
+ class Account(Base):
80
+ __tablename__ = "accounts"
81
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
82
+ user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
83
+ provider: Mapped[str] = mapped_column(String(50))
84
+ provider_account_id: Mapped[str] = mapped_column(String(255))
85
+ access_token: Mapped[str | None] = mapped_column(String(1000), nullable=True)
86
+ refresh_token: Mapped[str | None] = mapped_column(String(1000), nullable=True)
87
+ expires_at: Mapped[datetime | None] = mapped_column(nullable=True)
88
+ scope: Mapped[str | None] = mapped_column(String(500), nullable=True)
89
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
90
+
91
+
92
+ class Session(Base):
93
+ __tablename__ = "sessions"
94
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
95
+ user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
96
+ expires_at: Mapped[datetime] = mapped_column(index=True)
97
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
98
+
99
+
100
+ class OAuthState(Base):
101
+ __tablename__ = "oauth_states"
102
+ id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
103
+ state: Mapped[str] = mapped_column(String(255), unique=True, index=True)
104
+ expires_at: Mapped[datetime] = mapped_column(index=True)
105
+ created_at: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
106
+ ```
107
+
108
+ ### 2) Configure Belgie
109
+
110
+ ```python
111
+ from belgie.auth import Auth, AuthSettings, GoogleProviderSettings
112
+ from belgie_alchemy import AlchemyAdapter
113
+
114
+ settings = AuthSettings(
115
+ secret="your-secret-key",
116
+ base_url="http://localhost:8000",
117
+ )
118
+
119
+ adapter = AlchemyAdapter(
120
+ user=User,
121
+ account=Account,
122
+ session=Session,
123
+ oauth_state=OAuthState,
124
+ )
125
+
126
+ auth = Auth(
127
+ settings=settings,
128
+ adapter=adapter,
129
+ providers={
130
+ "google": GoogleProviderSettings(
131
+ client_id="your-google-client-id",
132
+ client_secret="your-google-client-secret",
133
+ redirect_uri="http://localhost:8000/auth/provider/google/callback",
134
+ scopes=["openid", "email", "profile"],
135
+ ),
136
+ },
137
+ )
138
+ ```
139
+
140
+ ### 3) Add routes to FastAPI
141
+
142
+ ```python
143
+ from fastapi import Depends, FastAPI, Security
144
+
145
+ app = FastAPI()
146
+ app.include_router(auth.router)
147
+
148
+
149
+ @app.get("/")
150
+ async def home():
151
+ return {"message": "Welcome! Visit /auth/provider/google/signin to sign in"}
152
+
153
+
154
+ @app.get("/protected")
155
+ async def protected(user: User = Depends(auth.user)):
156
+ return {"email": user.email}
157
+
158
+
159
+ @app.get("/profile")
160
+ async def profile(user: User = Security(auth.user, scopes=["profile"])):
161
+ return {"name": user.name, "email": user.email}
162
+ ```
163
+
164
+ Run it:
165
+
166
+ ```bash
167
+ uvicorn main:app --reload
168
+ ```
169
+
170
+ Visit `http://localhost:8000/auth/signin/google` to sign in.
171
+
172
+ ## Configuration shortcuts
173
+
174
+ - Environment variables: `BELGIE_SECRET`, `BELGIE_BASE_URL`, `BELGIE_GOOGLE_CLIENT_ID`, `BELGIE_GOOGLE_CLIENT_SECRET`,
175
+ `BELGIE_GOOGLE_REDIRECT_URI` (loaded automatically by `AuthSettings()`).
176
+ - Session tuning: `SessionSettings(cookie_name, max_age, update_age)` controls lifetime and sliding refresh.
177
+ - Cookie hardening: `CookieSettings(http_only, secure, same_site)` for production-ready defaults.
178
+
179
+ ## Router endpoints
180
+
181
+ - `GET /auth/signin/google` – start OAuth flow
182
+ - `GET /auth/callback/google` – handle Google callback
183
+ - `POST /auth/signout` – clear session cookie and invalidate server session
184
+
185
+ ## Limitations today
186
+
187
+ - Google is the only built-in provider; more providers and email/password are on the roadmap.
188
+ - You manage your own database migrations and deployment (by design—no third-party control plane).
189
+
190
+ ## Why teams pick Belgie
191
+
192
+ - Keep control of data and infra while getting a batteries-included OAuth flow.
193
+ - Minimal surface area: a single `Auth` instance exposes router + dependencies.
194
+ - Modern typing and clear protocols reduce integration mistakes and make refactors safer.
195
+ - MIT license, zero per-user costs.
196
+
197
+ ## Documentation and examples
198
+
199
+ - [docs/quickstart.md](docs/quickstart.md) for full walkthrough
200
+ - [examples/auth](examples/auth) for a runnable app
201
+
202
+ ## Contributing
203
+
204
+ MIT licensed. Issues and PRs welcome.
@@ -0,0 +1,138 @@
1
+ [project]
2
+ name = "belgie"
3
+ version = "0.1.0"
4
+ description = "Modern authentication for FastAPI"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Matt LeMay", email = "mplemay@users.noreply.github.com" }
8
+ ]
9
+ requires-python = ">=3.12,<3.15"
10
+ dependencies = [
11
+ "belgie-proto",
12
+ "fastapi>=0.100",
13
+ "httpx>=0.24",
14
+ "pydantic>=2.0",
15
+ "pydantic-settings>=2.0",
16
+ "python-multipart>=0.0.20",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ alchemy = [
21
+ "belgie-alchemy",
22
+ ]
23
+ all = [
24
+ "belgie-alchemy",
25
+ "belgie-mcp",
26
+ "belgie-oauth",
27
+ ]
28
+ examples = [
29
+ "uvicorn[standard]>=0.38.0",
30
+ ]
31
+ mcp = [
32
+ "belgie-mcp",
33
+ ]
34
+ oauth = [
35
+ "belgie-oauth",
36
+ ]
37
+
38
+ [build-system]
39
+ requires = ["uv_build>=0.9.10,<0.10.0"]
40
+ build-backend = "uv_build"
41
+
42
+ [dependency-groups]
43
+ dev = [
44
+ "aiosqlite>=0.21.0",
45
+ "belgie-alchemy",
46
+ "pre-commit>=4.4.0",
47
+ "pytest>=9.0.1",
48
+ "pytest-asyncio>=1.3.0",
49
+ "pytest-cov>=7.0.0",
50
+ "respx>=0.22.0",
51
+ "ruff>=0.14.5",
52
+ "rumdl",
53
+ "ty>=0.0.1a27",
54
+ ]
55
+
56
+ [tool.uv.sources]
57
+ belgie-alchemy = { workspace = true }
58
+ belgie-mcp = { workspace = true }
59
+ belgie-oauth = { workspace = true }
60
+ belgie-proto = { workspace = true }
61
+
62
+ [tool.uv.workspace]
63
+ members = [
64
+ "packages/belgie-alchemy",
65
+ "packages/belgie-mcp",
66
+ "packages/belgie-oauth",
67
+ "packages/belgie-proto",
68
+ ]
69
+
70
+ [tool.ruff]
71
+ target-version = "py312"
72
+ line-length = 120
73
+ include = ["pyproject.toml"]
74
+
75
+ [tool.ruff.lint]
76
+ select = ["ALL"]
77
+ ignore = [
78
+ "D", # pydocstyle (i.e. ignore missing/malformed docstrings)
79
+ ]
80
+
81
+ [tool.ruff.lint.per-file-ignores]
82
+ "__init__.py" = [
83
+ "F401", # imported but unused (common in __init__.py to re-export symbols)
84
+ ]
85
+ "**/tests/**/*" = [
86
+ "ANN001", # missing type annotation for function argument
87
+ "ANN201", # missing return type annotation for public function
88
+ "ANN202", # missing return type annotation for private function
89
+ "B008", # function call in argument defaults (required for FastAPI Depends)
90
+ "F841", # local variable assigned but never used
91
+ "FAST002", # FastAPI dependency without Annotated
92
+ "INP001", # implicit namespace package (tests dir may not need __init__.py)
93
+ "PLR0913", # too many arguments in function definition
94
+ "PLR2004", # magic value used in comparison (fine in test cases)
95
+ "S101", # use of assert (tests typically use assert directly)
96
+ "S106", # possible hardcoded password (test secrets are fine)
97
+ "SLF001", # private member accessed
98
+ ]
99
+ "**/__tests__/**/*" = [
100
+ "ANN001", # missing type annotation for function argument
101
+ "ANN201", # missing return type annotation for public function
102
+ "ANN202", # missing return type annotation for private function
103
+ "B008", # function call in argument defaults (required for FastAPI Depends)
104
+ "F841", # local variable assigned but never used
105
+ "FAST002", # FastAPI dependency without Annotated
106
+ "INP001", # implicit namespace package (tests dir may not need __init__.py)
107
+ "PLR0913", # too many arguments in function definition
108
+ "PLR2004", # magic value used in comparison (fine in test cases)
109
+ "S101", # use of assert (tests typically use assert directly)
110
+ "S106", # possible hardcoded password (test secrets are fine)
111
+ "SLF001", # private member accessed
112
+ ]
113
+
114
+ [tool.ruff.lint.flake8-quotes]
115
+ inline-quotes = "double"
116
+ multiline-quotes = "double"
117
+
118
+ [tool.ruff.lint.isort]
119
+ combine-as-imports = true
120
+
121
+ [tool.ruff.format]
122
+ docstring-code-format = true
123
+ quote-style = "double"
124
+
125
+ [tool.pytest.ini_options]
126
+ asyncio_mode = "strict"
127
+ markers = [
128
+ "integration: marks tests that hit the database or external services",
129
+ ]
130
+ filterwarnings = [
131
+ "error",
132
+ ]
133
+
134
+ [tool.rumdl.MD013]
135
+ line_length = 120
136
+ reflow = true
137
+ code_blocks = false
138
+ tables = false
Binary file
@@ -0,0 +1,97 @@
1
+ """Belgie - Modern authentication and analytics for FastAPI."""
2
+
3
+ from importlib import import_module
4
+ from typing import TYPE_CHECKING
5
+
6
+ from belgie.auth import (
7
+ Auth,
8
+ AuthClient,
9
+ AuthenticationError,
10
+ AuthorizationError,
11
+ AuthSettings,
12
+ BelgieError,
13
+ ConfigurationError,
14
+ CookieSettings,
15
+ DBConnection,
16
+ GoogleOAuthProvider,
17
+ GoogleProviderSettings,
18
+ GoogleUserInfo,
19
+ HookContext,
20
+ HookEvent,
21
+ HookRunner,
22
+ Hooks,
23
+ InvalidStateError,
24
+ OAuthError,
25
+ OAuthProviderProtocol,
26
+ Providers,
27
+ SessionExpiredError,
28
+ SessionManager,
29
+ SessionSettings,
30
+ URLSettings,
31
+ generate_session_id,
32
+ generate_state_token,
33
+ parse_scopes,
34
+ validate_scopes,
35
+ )
36
+
37
+ if TYPE_CHECKING:
38
+ from belgie_alchemy import AlchemyAdapter as AlchemyAdapter
39
+
40
+ __version__ = "0.1.0"
41
+
42
+ _ALCHEMY_IMPORT_ERROR = "AlchemyAdapter requires the 'alchemy' extra. Install with: uv add belgie[alchemy]"
43
+
44
+
45
+ def __getattr__(name: str) -> object:
46
+ if name != "AlchemyAdapter":
47
+ msg = f"module 'belgie' has no attribute {name!r}"
48
+ raise AttributeError(msg)
49
+
50
+ try:
51
+ module = import_module("belgie_alchemy")
52
+ except ModuleNotFoundError as exc:
53
+ raise ImportError(_ALCHEMY_IMPORT_ERROR) from exc
54
+
55
+ return module.AlchemyAdapter
56
+
57
+
58
+ __all__ = [ # noqa: RUF022
59
+ # Version
60
+ "__version__",
61
+ # Core
62
+ "Auth",
63
+ "AuthClient",
64
+ "AuthSettings",
65
+ "Hooks",
66
+ "HookContext",
67
+ "HookEvent",
68
+ "HookRunner",
69
+ # Adapters
70
+ "AlchemyAdapter",
71
+ "DBConnection",
72
+ # Session
73
+ "SessionManager",
74
+ # Providers
75
+ "GoogleOAuthProvider",
76
+ "GoogleProviderSettings",
77
+ "GoogleUserInfo",
78
+ "OAuthProviderProtocol",
79
+ "Providers",
80
+ # Settings
81
+ "SessionSettings",
82
+ "CookieSettings",
83
+ "URLSettings",
84
+ # Exceptions
85
+ "BelgieError",
86
+ "AuthenticationError",
87
+ "AuthorizationError",
88
+ "SessionExpiredError",
89
+ "InvalidStateError",
90
+ "OAuthError",
91
+ "ConfigurationError",
92
+ # Utils
93
+ "generate_session_id",
94
+ "generate_state_token",
95
+ "parse_scopes",
96
+ "validate_scopes",
97
+ ]
@@ -0,0 +1,24 @@
1
+ """Alchemy re-exports for belgie consumers."""
2
+
3
+ _ALCHEMY_IMPORT_ERROR = "belgie.alchemy requires the 'alchemy' extra. Install with: uv add belgie[alchemy]"
4
+
5
+ try:
6
+ from belgie_alchemy import ( # type: ignore[import-not-found]
7
+ AlchemyAdapter,
8
+ Base,
9
+ DatabaseSettings,
10
+ DateTimeUTC,
11
+ PrimaryKeyMixin,
12
+ TimestampMixin,
13
+ )
14
+ except ModuleNotFoundError as exc:
15
+ raise ImportError(_ALCHEMY_IMPORT_ERROR) from exc
16
+
17
+ __all__ = [
18
+ "AlchemyAdapter",
19
+ "Base",
20
+ "DatabaseSettings",
21
+ "DateTimeUTC",
22
+ "PrimaryKeyMixin",
23
+ "TimestampMixin",
24
+ ]