py-auth-sqlalchemy 0.0.2__tar.gz → 0.0.3__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.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-auth-sqlalchemy
3
+ Version: 0.0.3
4
+ Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
5
+ Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
8
+ Project-URL: Repository, https://github.com/jamaldeen09/py-auth
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: sqlalchemy>=2.0.0
20
+ Requires-Dist: greenlet>=3.0.0
21
+ Requires-Dist: py-auth-core>=0.0.3
22
+ Dynamic: license-file
23
+
24
+ # py-auth-sqlalchemy
25
+
26
+ Async SQLAlchemy adapter for [`py-auth-core`](https://pypi.org/project/py-auth-core/).
27
+
28
+ You bring your own SQLAlchemy models and an `AsyncEngine`. The adapter implements the py-auth storage contract: users, linked accounts, and sessions. Integrity errors are mapped to `py-auth-core` exceptions (`DuplicateEntryError`, `ForeignKeyViolationError`, and so on).
29
+
30
+ Requires Python 3.10+ and SQLAlchemy 2.0+.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install py-auth-sqlalchemy
36
+ ```
37
+
38
+ Install an async driver as well:
39
+
40
+ | Database | Driver | Example URL |
41
+ | ---------- | ---------- | ----------- |
42
+ | PostgreSQL | `asyncpg` | `postgresql+asyncpg://...` |
43
+ | MySQL | `aiomysql` | `mysql+aiomysql://...` |
44
+ | SQLite | `aiosqlite`| `sqlite+aiosqlite:///...` |
45
+
46
+ ```bash
47
+ pip install asyncpg # or aiomysql, or aiosqlite
48
+ ```
49
+
50
+ A sync engine is rejected. Only those three drivers are accepted.
51
+
52
+ ## Models
53
+
54
+ Pass your own SQLAlchemy declarative classes. On construction, the adapter checks that each provided model **defines the columns py-auth reads and writes internally**. That check is about **whether the column exists**, not about database constraints.
55
+
56
+ `nullable`, `unique`, `ForeignKey`, defaults, indexes, and extra columns are yours. You can make `email` unique or not, `name` nullable or not, and so on. The adapter does not inspect those.
57
+
58
+ If a required column is missing (for example a `Session` model with no `session_token_hash`), `validate_sqlalchemy_model` raises `AdapterError` immediately. py-auth looks those attributes up by name; without them, later operations would fail unpredictably.
59
+
60
+ **Session** is always required. **User** and **Account** are optional on the adapter; omit them if you do not need user or OAuth-account operations. Calling those methods without the matching model also raises `AdapterError`. Extra columns beyond the lists below are allowed.
61
+
62
+ | Model | When you pass it | Required column *names* |
63
+ | --- | --- | --- |
64
+ | Session | Always | `id`, `session_token_hash`, `user_id`, `expires` |
65
+ | User | User CRUD / credentials | `id`, `email`, `name`, `image`, `password_hash` |
66
+ | Account | OAuth account linking | `id`, `user_id`, `type`, `provider`, `provider_account_id`, `access_token`, `refresh_token`, `expires_at`, `token_type`, `scope`, `id_token`, `session_state` |
67
+
68
+ Use `UTCDateTime` for `expires` so values stay timezone-aware UTC across backends. Naive datetimes are rejected. Constraints in the example below (`unique=True`, `nullable=...`) are typical choices, not adapter rules.
69
+
70
+ ```python
71
+ from uuid import uuid4
72
+ from datetime import datetime
73
+ from sqlalchemy import ForeignKey, String, UniqueConstraint
74
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
75
+ from py_auth_sqlalchemy import UTCDateTime
76
+
77
+ class Base(DeclarativeBase):
78
+ pass
79
+
80
+ class User(Base):
81
+ __tablename__ = "users"
82
+
83
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
84
+ email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
85
+ name: Mapped[str | None] = mapped_column(String(255), nullable=True)
86
+ image: Mapped[str | None] = mapped_column(String(500), nullable=True)
87
+ password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
88
+
89
+ class Session(Base):
90
+ __tablename__ = "sessions"
91
+
92
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
93
+ session_token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
94
+ user_id: Mapped[str] = mapped_column(String(255), ForeignKey("users.id"), nullable=False)
95
+ expires: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
96
+
97
+ class Account(Base):
98
+ __tablename__ = "accounts"
99
+
100
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
101
+ user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), nullable=False)
102
+ type: Mapped[str] = mapped_column(String(50), nullable=False)
103
+ provider: Mapped[str] = mapped_column(String(100), nullable=False)
104
+ provider_account_id: Mapped[str] = mapped_column(String(255), nullable=False)
105
+ access_token: Mapped[str | None] = mapped_column(nullable=True)
106
+ refresh_token: Mapped[str | None] = mapped_column(nullable=True)
107
+ expires_at: Mapped[int | None] = mapped_column(nullable=True)
108
+ token_type: Mapped[str | None] = mapped_column(nullable=True)
109
+ scope: Mapped[str | None] = mapped_column(nullable=True)
110
+ id_token: Mapped[str | None] = mapped_column(nullable=True)
111
+ session_state: Mapped[str | None] = mapped_column(nullable=True)
112
+
113
+ __table_args__ = (
114
+ UniqueConstraint("provider", "provider_account_id", name="uq_accounts_provider_account"),
115
+ )
116
+ ```
117
+
118
+ ## Usage
119
+
120
+ ```python
121
+ from sqlalchemy.ext.asyncio import create_async_engine
122
+ from py_auth import PyAuth
123
+ from py_auth_sqlalchemy import SqlAlchemyAdapter
124
+
125
+ engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
126
+
127
+ adapter = SqlAlchemyAdapter(
128
+ engine=engine,
129
+ session_model=Session,
130
+ user_model=User,
131
+ account_model=Account,
132
+ )
133
+
134
+ auth = PyAuth(adapter=adapter, providers=[...])
135
+ ```
136
+
137
+ Rows come back as dicts keyed by column name. Create the tables yourself (`Base.metadata.create_all` or Alembic); this package does not run migrations.
138
+
139
+ ## License
140
+
141
+ MIT
@@ -0,0 +1,118 @@
1
+ # py-auth-sqlalchemy
2
+
3
+ Async SQLAlchemy adapter for [`py-auth-core`](https://pypi.org/project/py-auth-core/).
4
+
5
+ You bring your own SQLAlchemy models and an `AsyncEngine`. The adapter implements the py-auth storage contract: users, linked accounts, and sessions. Integrity errors are mapped to `py-auth-core` exceptions (`DuplicateEntryError`, `ForeignKeyViolationError`, and so on).
6
+
7
+ Requires Python 3.10+ and SQLAlchemy 2.0+.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install py-auth-sqlalchemy
13
+ ```
14
+
15
+ Install an async driver as well:
16
+
17
+ | Database | Driver | Example URL |
18
+ | ---------- | ---------- | ----------- |
19
+ | PostgreSQL | `asyncpg` | `postgresql+asyncpg://...` |
20
+ | MySQL | `aiomysql` | `mysql+aiomysql://...` |
21
+ | SQLite | `aiosqlite`| `sqlite+aiosqlite:///...` |
22
+
23
+ ```bash
24
+ pip install asyncpg # or aiomysql, or aiosqlite
25
+ ```
26
+
27
+ A sync engine is rejected. Only those three drivers are accepted.
28
+
29
+ ## Models
30
+
31
+ Pass your own SQLAlchemy declarative classes. On construction, the adapter checks that each provided model **defines the columns py-auth reads and writes internally**. That check is about **whether the column exists**, not about database constraints.
32
+
33
+ `nullable`, `unique`, `ForeignKey`, defaults, indexes, and extra columns are yours. You can make `email` unique or not, `name` nullable or not, and so on. The adapter does not inspect those.
34
+
35
+ If a required column is missing (for example a `Session` model with no `session_token_hash`), `validate_sqlalchemy_model` raises `AdapterError` immediately. py-auth looks those attributes up by name; without them, later operations would fail unpredictably.
36
+
37
+ **Session** is always required. **User** and **Account** are optional on the adapter; omit them if you do not need user or OAuth-account operations. Calling those methods without the matching model also raises `AdapterError`. Extra columns beyond the lists below are allowed.
38
+
39
+ | Model | When you pass it | Required column *names* |
40
+ | --- | --- | --- |
41
+ | Session | Always | `id`, `session_token_hash`, `user_id`, `expires` |
42
+ | User | User CRUD / credentials | `id`, `email`, `name`, `image`, `password_hash` |
43
+ | Account | OAuth account linking | `id`, `user_id`, `type`, `provider`, `provider_account_id`, `access_token`, `refresh_token`, `expires_at`, `token_type`, `scope`, `id_token`, `session_state` |
44
+
45
+ Use `UTCDateTime` for `expires` so values stay timezone-aware UTC across backends. Naive datetimes are rejected. Constraints in the example below (`unique=True`, `nullable=...`) are typical choices, not adapter rules.
46
+
47
+ ```python
48
+ from uuid import uuid4
49
+ from datetime import datetime
50
+ from sqlalchemy import ForeignKey, String, UniqueConstraint
51
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
52
+ from py_auth_sqlalchemy import UTCDateTime
53
+
54
+ class Base(DeclarativeBase):
55
+ pass
56
+
57
+ class User(Base):
58
+ __tablename__ = "users"
59
+
60
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
61
+ email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
62
+ name: Mapped[str | None] = mapped_column(String(255), nullable=True)
63
+ image: Mapped[str | None] = mapped_column(String(500), nullable=True)
64
+ password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
65
+
66
+ class Session(Base):
67
+ __tablename__ = "sessions"
68
+
69
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
70
+ session_token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
71
+ user_id: Mapped[str] = mapped_column(String(255), ForeignKey("users.id"), nullable=False)
72
+ expires: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
73
+
74
+ class Account(Base):
75
+ __tablename__ = "accounts"
76
+
77
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
78
+ user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), nullable=False)
79
+ type: Mapped[str] = mapped_column(String(50), nullable=False)
80
+ provider: Mapped[str] = mapped_column(String(100), nullable=False)
81
+ provider_account_id: Mapped[str] = mapped_column(String(255), nullable=False)
82
+ access_token: Mapped[str | None] = mapped_column(nullable=True)
83
+ refresh_token: Mapped[str | None] = mapped_column(nullable=True)
84
+ expires_at: Mapped[int | None] = mapped_column(nullable=True)
85
+ token_type: Mapped[str | None] = mapped_column(nullable=True)
86
+ scope: Mapped[str | None] = mapped_column(nullable=True)
87
+ id_token: Mapped[str | None] = mapped_column(nullable=True)
88
+ session_state: Mapped[str | None] = mapped_column(nullable=True)
89
+
90
+ __table_args__ = (
91
+ UniqueConstraint("provider", "provider_account_id", name="uq_accounts_provider_account"),
92
+ )
93
+ ```
94
+
95
+ ## Usage
96
+
97
+ ```python
98
+ from sqlalchemy.ext.asyncio import create_async_engine
99
+ from py_auth import PyAuth
100
+ from py_auth_sqlalchemy import SqlAlchemyAdapter
101
+
102
+ engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
103
+
104
+ adapter = SqlAlchemyAdapter(
105
+ engine=engine,
106
+ session_model=Session,
107
+ user_model=User,
108
+ account_model=Account,
109
+ )
110
+
111
+ auth = PyAuth(adapter=adapter, providers=[...])
112
+ ```
113
+
114
+ Rows come back as dicts keyed by column name. Create the tables yourself (`Base.metadata.create_all` or Alembic); this package does not run migrations.
115
+
116
+ ## License
117
+
118
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "py-auth-sqlalchemy"
7
- version = "0.0.2"
7
+ version = "0.0.3"
8
8
  authors = [
9
9
  { name = "Olatunji Jamaldeen Omotoyosi", email = "jamaldeen.o@yahoo.com" },
10
10
  ]
@@ -5,4 +5,4 @@ from .core import SqlAlchemyAdapter
5
5
  from .utc_datetime import UTCDateTime
6
6
 
7
7
  __all__ = ["SqlAlchemyAdapter", "UTCDateTime"]
8
- __version__ = "0.0.2"
8
+ __version__ = "0.0.3"
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-auth-sqlalchemy
3
+ Version: 0.0.3
4
+ Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
5
+ Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
8
+ Project-URL: Repository, https://github.com/jamaldeen09/py-auth
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: sqlalchemy>=2.0.0
20
+ Requires-Dist: greenlet>=3.0.0
21
+ Requires-Dist: py-auth-core>=0.0.3
22
+ Dynamic: license-file
23
+
24
+ # py-auth-sqlalchemy
25
+
26
+ Async SQLAlchemy adapter for [`py-auth-core`](https://pypi.org/project/py-auth-core/).
27
+
28
+ You bring your own SQLAlchemy models and an `AsyncEngine`. The adapter implements the py-auth storage contract: users, linked accounts, and sessions. Integrity errors are mapped to `py-auth-core` exceptions (`DuplicateEntryError`, `ForeignKeyViolationError`, and so on).
29
+
30
+ Requires Python 3.10+ and SQLAlchemy 2.0+.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install py-auth-sqlalchemy
36
+ ```
37
+
38
+ Install an async driver as well:
39
+
40
+ | Database | Driver | Example URL |
41
+ | ---------- | ---------- | ----------- |
42
+ | PostgreSQL | `asyncpg` | `postgresql+asyncpg://...` |
43
+ | MySQL | `aiomysql` | `mysql+aiomysql://...` |
44
+ | SQLite | `aiosqlite`| `sqlite+aiosqlite:///...` |
45
+
46
+ ```bash
47
+ pip install asyncpg # or aiomysql, or aiosqlite
48
+ ```
49
+
50
+ A sync engine is rejected. Only those three drivers are accepted.
51
+
52
+ ## Models
53
+
54
+ Pass your own SQLAlchemy declarative classes. On construction, the adapter checks that each provided model **defines the columns py-auth reads and writes internally**. That check is about **whether the column exists**, not about database constraints.
55
+
56
+ `nullable`, `unique`, `ForeignKey`, defaults, indexes, and extra columns are yours. You can make `email` unique or not, `name` nullable or not, and so on. The adapter does not inspect those.
57
+
58
+ If a required column is missing (for example a `Session` model with no `session_token_hash`), `validate_sqlalchemy_model` raises `AdapterError` immediately. py-auth looks those attributes up by name; without them, later operations would fail unpredictably.
59
+
60
+ **Session** is always required. **User** and **Account** are optional on the adapter; omit them if you do not need user or OAuth-account operations. Calling those methods without the matching model also raises `AdapterError`. Extra columns beyond the lists below are allowed.
61
+
62
+ | Model | When you pass it | Required column *names* |
63
+ | --- | --- | --- |
64
+ | Session | Always | `id`, `session_token_hash`, `user_id`, `expires` |
65
+ | User | User CRUD / credentials | `id`, `email`, `name`, `image`, `password_hash` |
66
+ | Account | OAuth account linking | `id`, `user_id`, `type`, `provider`, `provider_account_id`, `access_token`, `refresh_token`, `expires_at`, `token_type`, `scope`, `id_token`, `session_state` |
67
+
68
+ Use `UTCDateTime` for `expires` so values stay timezone-aware UTC across backends. Naive datetimes are rejected. Constraints in the example below (`unique=True`, `nullable=...`) are typical choices, not adapter rules.
69
+
70
+ ```python
71
+ from uuid import uuid4
72
+ from datetime import datetime
73
+ from sqlalchemy import ForeignKey, String, UniqueConstraint
74
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
75
+ from py_auth_sqlalchemy import UTCDateTime
76
+
77
+ class Base(DeclarativeBase):
78
+ pass
79
+
80
+ class User(Base):
81
+ __tablename__ = "users"
82
+
83
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
84
+ email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
85
+ name: Mapped[str | None] = mapped_column(String(255), nullable=True)
86
+ image: Mapped[str | None] = mapped_column(String(500), nullable=True)
87
+ password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
88
+
89
+ class Session(Base):
90
+ __tablename__ = "sessions"
91
+
92
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
93
+ session_token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
94
+ user_id: Mapped[str] = mapped_column(String(255), ForeignKey("users.id"), nullable=False)
95
+ expires: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
96
+
97
+ class Account(Base):
98
+ __tablename__ = "accounts"
99
+
100
+ id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
101
+ user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), nullable=False)
102
+ type: Mapped[str] = mapped_column(String(50), nullable=False)
103
+ provider: Mapped[str] = mapped_column(String(100), nullable=False)
104
+ provider_account_id: Mapped[str] = mapped_column(String(255), nullable=False)
105
+ access_token: Mapped[str | None] = mapped_column(nullable=True)
106
+ refresh_token: Mapped[str | None] = mapped_column(nullable=True)
107
+ expires_at: Mapped[int | None] = mapped_column(nullable=True)
108
+ token_type: Mapped[str | None] = mapped_column(nullable=True)
109
+ scope: Mapped[str | None] = mapped_column(nullable=True)
110
+ id_token: Mapped[str | None] = mapped_column(nullable=True)
111
+ session_state: Mapped[str | None] = mapped_column(nullable=True)
112
+
113
+ __table_args__ = (
114
+ UniqueConstraint("provider", "provider_account_id", name="uq_accounts_provider_account"),
115
+ )
116
+ ```
117
+
118
+ ## Usage
119
+
120
+ ```python
121
+ from sqlalchemy.ext.asyncio import create_async_engine
122
+ from py_auth import PyAuth
123
+ from py_auth_sqlalchemy import SqlAlchemyAdapter
124
+
125
+ engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
126
+
127
+ adapter = SqlAlchemyAdapter(
128
+ engine=engine,
129
+ session_model=Session,
130
+ user_model=User,
131
+ account_model=Account,
132
+ )
133
+
134
+ auth = PyAuth(adapter=adapter, providers=[...])
135
+ ```
136
+
137
+ Rows come back as dicts keyed by column name. Create the tables yourself (`Base.metadata.create_all` or Alembic); this package does not run migrations.
138
+
139
+ ## License
140
+
141
+ MIT
@@ -1,22 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: py-auth-sqlalchemy
3
- Version: 0.0.2
4
- Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
5
- Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
8
- Project-URL: Repository, https://github.com/jamaldeen09/py-auth
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: 3.12
13
- Classifier: Programming Language :: Python :: 3.13
14
- Classifier: Operating System :: OS Independent
15
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
- Requires-Python: >=3.10
17
- Description-Content-Type: text/markdown
18
- License-File: LICENSE
19
- Requires-Dist: sqlalchemy>=2.0.0
20
- Requires-Dist: greenlet>=3.0.0
21
- Requires-Dist: py-auth-core>=0.0.3
22
- Dynamic: license-file
File without changes
@@ -1,22 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: py-auth-sqlalchemy
3
- Version: 0.0.2
4
- Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
5
- Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
8
- Project-URL: Repository, https://github.com/jamaldeen09/py-auth
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: 3.12
13
- Classifier: Programming Language :: Python :: 3.13
14
- Classifier: Operating System :: OS Independent
15
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
- Requires-Python: >=3.10
17
- Description-Content-Type: text/markdown
18
- License-File: LICENSE
19
- Requires-Dist: sqlalchemy>=2.0.0
20
- Requires-Dist: greenlet>=3.0.0
21
- Requires-Dist: py-auth-core>=0.0.3
22
- Dynamic: license-file