Skip to content

Commit 6becf21

Browse files
committed
Add type annotations
- Add CI to check these work for oldest and newest supported Python versions - Add the py.typed marker file
1 parent 0b0c7dc commit 6becf21

File tree

11 files changed

+67
-28
lines changed

11 files changed

+67
-28
lines changed

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,25 @@ jobs:
116116
runs-on: ubuntu-latest
117117
steps:
118118
- uses: cjw296/python-action/check-coverage@v1
119+
120+
typing:
121+
name: python ${{ matrix.python-version }} type checking
122+
runs-on: ubuntu-latest
123+
strategy:
124+
matrix:
125+
python-version:
126+
- "3.9"
127+
- "3.13"
128+
steps:
129+
- uses: actions/checkout@v4
130+
131+
- uses: astral-sh/setup-uv@v5
132+
with:
133+
python-version: ${{ matrix.python-version }}
134+
cache-dependency-glob: "**/pyproject.toml"
135+
136+
- name: Install the project
137+
run: uv sync --all-extras --dev
138+
139+
- name: Run tests
140+
run: uv run mypy pytest_sqlalchemy.py tests

pyproject.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ sqlalchemy = "pytest_sqlalchemy"
2929

3030
[dependency-groups]
3131
dev = [
32+
"mypy>=1.15.0",
3233
"pytest-xdist>=3.6.1",
3334
]
35+
3436
[build-system]
3537
requires = ["setuptools>=61.0"]
3638
build-backend = "setuptools.build_meta"
@@ -39,5 +41,13 @@ build-backend = "setuptools.build_meta"
3941
source = ["pytest_sqlalchemy", "tests"]
4042
parallel = true
4143

44+
[tool.mypy]
45+
disallow_untyped_defs = true
46+
disallow_incomplete_defs = true
47+
48+
[[tool.mypy.overrides]]
49+
module = "sqlalchemy_utils.*"
50+
ignore_missing_imports = true
51+
4252
[tool.pytest.ini_options]
4353
norecursedirs = ["tests/examples"]

pytest_sqlalchemy.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
#!/usr/bin/env python
22
# encoding: utf-8
33

4+
from typing import Any, Dict, Mapping, Optional
5+
46
import pytest
57
import sqlalchemy_utils.functions
8+
from pytest import FixtureRequest, Parser
9+
from sqlalchemy import engine_from_config
10+
from sqlalchemy.engine import Connection, create_engine, Engine
611

712

813
@pytest.fixture(scope="session")
9-
def engine(request, sqlalchemy_connect_url, app_config):
14+
def engine(request: FixtureRequest, sqlalchemy_connect_url: Optional[str], app_config: Optional[Dict[str, str]]) -> Engine:
1015
"""Engine configuration.
1116
See http://docs.sqlalchemy.org/en/latest/core/engines.html
1217
for more details.
@@ -19,10 +24,8 @@ def engine(request, sqlalchemy_connect_url, app_config):
1924
2025
"""
2126
if app_config:
22-
from sqlalchemy import engine_from_config
2327
engine = engine_from_config(app_config)
2428
elif sqlalchemy_connect_url:
25-
from sqlalchemy.engine import create_engine
2629
engine = create_engine(sqlalchemy_connect_url)
2730
else:
2831
raise RuntimeError("Can not establish a connection to the database")
@@ -34,7 +37,7 @@ def engine(request, sqlalchemy_connect_url, app_config):
3437
url = engine.url.set(database=f'{engine.url.database}_{xdist_suffix}')
3538
engine = create_engine(url) # override engine
3639

37-
def fin():
40+
def fin() -> None:
3841
print ("Disposing engine")
3942
engine.dispose()
4043

@@ -43,7 +46,7 @@ def fin():
4346

4447

4548
@pytest.fixture(scope="session")
46-
def db_schema(request, engine, sqlalchemy_manage_db, sqlalchemy_keep_db):
49+
def db_schema(request: FixtureRequest, engine: Engine, sqlalchemy_manage_db: bool, sqlalchemy_keep_db: bool) -> Optional[None]:
4750
if not sqlalchemy_manage_db:
4851
return
4952

@@ -55,18 +58,18 @@ def db_schema(request, engine, sqlalchemy_manage_db, sqlalchemy_keep_db):
5558
sqlalchemy_utils.functions.create_database(engine.url)
5659

5760
if not sqlalchemy_keep_db:
58-
def fin():
61+
def fin() -> None:
5962
print ("Tearing down DB")
6063
sqlalchemy_utils.functions.drop_database(engine.url)
6164

6265
request.addfinalizer(fin)
6366

6467

6568
@pytest.fixture(scope="module")
66-
def connection(request, engine, db_schema):
69+
def connection(request: FixtureRequest, engine: Engine, db_schema: Optional[None]) -> Connection:
6770
connection = engine.connect()
6871

69-
def fin():
72+
def fin() -> None:
7073
print ("Closing connection")
7174
connection.close()
7275

@@ -75,12 +78,12 @@ def fin():
7578

7679

7780
@pytest.fixture()
78-
def transaction(request, connection):
81+
def transaction(request: FixtureRequest, connection: Connection) -> Connection:
7982
"""Will start a transaction on the connection. The connection will
8083
be rolled back after it leaves its scope."""
8184
transaction = connection.begin()
8285

83-
def fin():
86+
def fin() -> None:
8487
print ("Rollback")
8588
transaction.rollback()
8689

@@ -89,34 +92,34 @@ def fin():
8992

9093

9194
@pytest.fixture()
92-
def dbsession(request, connection):
95+
def dbsession(request: FixtureRequest, connection: Connection) -> Any:
9396
from sqlalchemy.orm import sessionmaker
9497
return sessionmaker()(bind=connection)
9598

9699

97100
# Config options
98101
@pytest.fixture(scope="session")
99-
def sqlalchemy_connect_url(request):
102+
def sqlalchemy_connect_url(request: FixtureRequest) -> Optional[str]:
100103
return request.config.getoption("--sqlalchemy-connect-url")
101104

102105

103106
@pytest.fixture(scope="session")
104-
def connect_uri(request, sqlalchemy_connect_url):
107+
def connect_uri(request: FixtureRequest, sqlalchemy_connect_url: Optional[str]) -> Optional[str]:
105108
return sqlalchemy_connect_url
106109

107110

108111
@pytest.fixture(scope="session")
109-
def sqlalchemy_manage_db(request):
112+
def sqlalchemy_manage_db(request: FixtureRequest) -> Optional[bool]:
110113
return request.config.getoption("--sqlalchemy-manage-db")
111114

112115

113116
@pytest.fixture(scope="session")
114-
def sqlalchemy_keep_db(request):
117+
def sqlalchemy_keep_db(request: FixtureRequest) -> Optional[bool]:
115118
return request.config.getoption("--sqlalchemy-keep-db")
116119

117120

118121
@pytest.fixture(scope="session")
119-
def app_config(request):
122+
def app_config(request: FixtureRequest) -> Optional[Mapping[str, str]]:
120123
"""Example of a config file:
121124
122125
[DEFAULT]
@@ -132,7 +135,7 @@ def app_config(request):
132135
return None
133136

134137

135-
def pytest_addoption(parser):
138+
def pytest_addoption(parser: Parser) -> None:
136139
parser.addoption("--sqlalchemy-connect-url", action="store",
137140
default=None,
138141
help="Name of the database to connect to")

tests/examples/test_connect_uri.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
def test_uri(connect_uri):
1+
def test_uri(connect_uri: str) -> None:
22
assert isinstance(connect_uri, str)

tests/examples/test_db_schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from sqlalchemy.engine import Connection
1+
from sqlalchemy.engine import Engine
22
from sqlalchemy_utils import database_exists
33

44

5-
def test_db_schema(db_schema, engine):
5+
def test_db_schema(db_schema: None, engine: Engine) -> None:
66
assert db_schema is None # Not very helpful?
77
assert database_exists(engine.url)

tests/examples/test_dbsession.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from sqlalchemy.orm import Session
22

33

4-
def test_session(dbsession):
4+
def test_session(dbsession: Session) -> None:
55
assert isinstance(dbsession, Session)

tests/examples/test_minimal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from sqlalchemy.engine import Connection
22

33

4-
def test_connection(connection):
4+
def test_connection(connection: Connection) -> None:
55
assert isinstance(connection, Connection)
66
assert not connection.in_transaction()

tests/examples/test_transaction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from sqlalchemy.engine import Connection
22

33

4-
def test_transaction(transaction):
4+
def test_transaction(transaction: Connection) -> None:
55
assert isinstance(transaction, Connection)
66
assert transaction.in_transaction()

tests/examples/test_xdist.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
def test_one(engine):
2-
assert '_gw' in engine.url.database
1+
from sqlalchemy.engine import Engine
32

4-
def test_two(engine):
5-
assert '_gw' in engine.url.database
3+
def test_one(engine: Engine) -> None:
4+
db_name = str(engine.url.database) if engine.url.database is not None else ""
5+
assert '_gw' in db_name
6+
7+
def test_two(engine: Engine) -> None:
8+
db_name = str(engine.url.database) if engine.url.database is not None else ""
9+
assert '_gw' in db_name

tests/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)