Skip to content

Commit 565385d

Browse files
committed
Update version to 1.0.3, enhance project description, remove support for Python 3.9, and update type hints for compatibility with Python 3.10. Also, upgrade pytest to version 9.* and adjust CI configuration accordingly.
1 parent 48ee483 commit 565385d

File tree

13 files changed

+120
-782
lines changed

13 files changed

+120
-782
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
python-version: ["3.9", "3.14"]
14+
python-version: ["3.10", "3.14"]
1515

1616
steps:
1717
- uses: actions/checkout@v4
@@ -37,7 +37,7 @@ jobs:
3737
runs-on: ubuntu-latest
3838
strategy:
3939
matrix:
40-
python-version: ["3.9", "3.14"]
40+
python-version: ["3.10", "3.14"]
4141

4242
steps:
4343
- uses: actions/checkout@v4
@@ -63,7 +63,7 @@ jobs:
6363
runs-on: ubuntu-latest
6464
strategy:
6565
matrix:
66-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
66+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
6767
tests-command: ["test", "test-sqlalchemy14"]
6868

6969
services:

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
# Changelog
22

3+
## [1.0.3]
4+
5+
15/11/2025
6+
7+
- Improve project description.
8+
- Remove support for Python 3.9.
9+
- Update type hint syntax to be compatible with Python 3.10.
10+
- Update pytest to 9.*.
11+
312
## [1.0.2]
413

14+
15/11/2025
15+
516
- Fix issus with sqlalchemy-utils being pinned too strictly.
617
- Fix tox issues.
718
- Remove print statements from tests.
819
- Fix issues with type hints.
920

1021
## [1.0.0]
1122

23+
- Version 1.0.0.
24+
- This version breaks compatibility with any previous versions of the library.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# SQLAlchemy Diff
22

3-
A tool for comparing SQLAlchemy models.
3+
A tool for comparing database schemas using SQLAlchemy.
44

55
## Requirements
66

7-
- Python 3.9 or higher (supports 3.9, 3.10, 3.11, 3.12, 3.13, 3.14)
7+
- Python 3.10 or higher (supports 3.10, 3.11, 3.12, 3.13, 3.14)
88
- SQLAlchemy >= 1.4
99
- sqlalchemy-utils ~= 0.41.2
1010

@@ -100,7 +100,7 @@ class MyCustomInspector(BaseInspector, DiffMixin):
100100
# Set to False if it operates at table level (like columns, indexes)
101101
db_level = False
102102

103-
def inspect(self, engine: Engine, ignore_specs: Optional[list] = None) -> dict:
103+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
104104
"""
105105
Inspect the database and return structured data.
106106
@@ -161,7 +161,7 @@ class SequencesInspector(BaseInspector, DiffMixin):
161161
key = "sequences"
162162
db_level = True
163163

164-
def inspect(self, engine: Engine, ignore_specs: Optional[list] = None) -> dict:
164+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
165165
ignore_clauses = self._filter_ignorers(ignore_specs)
166166
inspector = self._get_inspector(engine)
167167

pyproject.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
[project]
22
name = "sqlalchemy-diff"
3-
version = "1.0.2"
3+
version = "1.0.3"
44
authors = [
55
{ name = "Fabrizio Romano", email = "[email protected]" },
66
{ name = "Mark McArdle", email = "[email protected]" },
77
]
8-
description = "A tool for comparing SQLAlchemy models"
8+
description = "A tool for comparing database schemas using SQLAlchemy"
99
readme = "README.md"
1010
license-files = ["LICEN[CS]E*"]
11-
requires-python = ">=3.9"
11+
requires-python = ">=3.10"
1212
classifiers = [
1313
"Programming Language :: Python :: 3",
14-
"Programming Language :: Python :: 3.9",
1514
"Programming Language :: Python :: 3.10",
1615
"Programming Language :: Python :: 3.11",
1716
"Programming Language :: Python :: 3.12",
@@ -46,7 +45,7 @@ lint = [
4645
"ty",
4746
]
4847
test = [
49-
"pytest~=8.4.2",
48+
"pytest~=9.0.1",
5049
"pytest-cov~=7.0.0",
5150
"psycopg2-binary",
5251
"pytest-xdist>=3.8.0",
@@ -82,7 +81,7 @@ line-length = 100
8281

8382
[tool.ruff]
8483
line-length = 100
85-
target-version = "py39"
84+
target-version = "py310"
8685

8786
[tool.ruff.format]
8887
# Like Black, use double quotes for strings.

src/sqlalchemydiff/comparer.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
from collections.abc import Iterable
44
from copy import deepcopy
5-
from typing import Any, Optional
5+
from typing import Any
66

77
from sqlalchemy.engine import Engine
88

@@ -104,8 +104,8 @@ def from_params(
104104
cls,
105105
db_one_uri: str,
106106
db_two_uri: str,
107-
db_one_params: Optional[dict[str, Any]] = None,
108-
db_two_params: Optional[dict[str, Any]] = None,
107+
db_one_params: dict[str, Any] | None = None,
108+
db_two_params: dict[str, Any] | None = None,
109109
):
110110
db_one_params = db_one_params or {}
111111
db_two_params = db_two_params or {}
@@ -118,8 +118,8 @@ def compare(
118118
self,
119119
one_alias: str = "one",
120120
two_alias: str = "two",
121-
ignores: Optional[list[str]] = None,
122-
ignore_inspectors: Optional[Iterable[str]] = None,
121+
ignores: list[str] | None = None,
122+
ignore_inspectors: Iterable[str] | None = None,
123123
):
124124
ignore_specs = self.ignore_spec_factory_class().create_specs(register, ignores)
125125

@@ -139,7 +139,7 @@ def compare(
139139
return self.compare_result_class(result, one_alias=one_alias, two_alias=two_alias)
140140

141141
def _filter_inspectors(
142-
self, ignore_inspectors: Optional[set[str]]
142+
self, ignore_inspectors: set[str] | None
143143
) -> list[tuple[str, type[BaseInspector]]]:
144144
if not ignore_inspectors:
145145
ignore_inspectors = set()
@@ -152,7 +152,7 @@ def _filter_inspectors(
152152

153153
def _get_db_info(
154154
self, ignore_specs: list[IgnoreSpecType], inspector: BaseInspector, engine: Engine
155-
) -> Optional[dict]:
155+
) -> dict | None:
156156
try:
157157
return inspector.inspect(engine, ignore_specs)
158158
except InspectorNotSupported as e:

src/sqlalchemydiff/inspection/base.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import abc
22
import inspect as stdlib_inspect
3-
from typing import Optional
43

54
from sqlalchemy import inspect
65
from sqlalchemy.engine import Engine
@@ -59,7 +58,7 @@ def __init__(self, one_alias: str = "one", two_alias: str = "two"):
5958

6059
@abc.abstractmethod
6160
def inspect(
62-
self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None
61+
self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None
6362
) -> dict: ... # pragma: no cover
6463

6564
@abc.abstractmethod
@@ -68,7 +67,7 @@ def diff(self, one: dict, two: dict) -> dict: ... # pragma: no cover
6867
@abc.abstractmethod
6968
def _is_supported(self, inspector: Inspector) -> bool: ... # pragma: no cover
7069

71-
def _filter_ignorers(self, specs: Optional[list[IgnoreSpecType]]) -> IgnoreClauses:
70+
def _filter_ignorers(self, specs: list[IgnoreSpecType] | None) -> IgnoreClauses:
7271
tables, enums, clauses = [], [], []
7372

7473
for spec in specs or []:

src/sqlalchemydiff/inspection/ignore.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
from dataclasses import dataclass, field
2-
from typing import NamedTuple, Optional, Union
2+
from typing import NamedTuple
33

44

55
class TableIgnoreSpec(NamedTuple):
66
table_name: str
7-
inspector_key: Optional[str] = None
8-
object_name: Optional[str] = None
7+
inspector_key: str | None = None
8+
object_name: str | None = None
99

1010

1111
class EnumIgnoreSpec(NamedTuple):
1212
name: str
1313

1414

15-
IgnoreSpecType = Union[TableIgnoreSpec, EnumIgnoreSpec]
15+
IgnoreSpecType = TableIgnoreSpec | EnumIgnoreSpec
1616

1717

1818
class IgnoreSpecFactory:
@@ -29,9 +29,7 @@ class IgnoreSpecFactory:
2929
separator = "."
3030

3131
@classmethod
32-
def create_specs(
33-
cls, register: dict, ignores: Optional[list[str]] = None
34-
) -> list[IgnoreSpecType]:
32+
def create_specs(cls, register: dict, ignores: list[str] | None = None) -> list[IgnoreSpecType]:
3533
if not ignores:
3634
return []
3735

@@ -64,6 +62,6 @@ class IgnoreClauses:
6462
enums: list[str] = field(default_factory=list)
6563
clauses: list[IgnoreSpecType] = field(default_factory=list)
6664

67-
def is_clause(self, table_name: str, inspector_key: str, object_name: Optional[str]) -> bool:
65+
def is_clause(self, table_name: str, inspector_key: str, object_name: str | None) -> bool:
6866
clause = TableIgnoreSpec(table_name, inspector_key, object_name)
6967
return clause in self.clauses

src/sqlalchemydiff/inspection/inspectors.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, cast
1+
from typing import cast
22

33
from sqlalchemy.engine import Engine
44

@@ -14,11 +14,11 @@ class TablesInspector(BaseInspector, DiffMixin):
1414
key = "tables"
1515
db_level = True
1616

17-
def inspect(self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None) -> dict:
17+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
1818
ignore_clauses = self._filter_ignorers(ignore_specs)
1919
inspector = self._get_inspector(engine)
2020

21-
def get_comment(table_name: str) -> Optional[str]:
21+
def get_comment(table_name: str) -> str | None:
2222
try:
2323
return inspector.get_table_comment(table_name)["text"]
2424
except NotImplementedError:
@@ -30,7 +30,7 @@ def get_comment(table_name: str) -> Optional[str]:
3030
if table_name not in ignore_clauses.tables
3131
}
3232

33-
def _format_table(self, table_name: str, comment: Optional[str] = None) -> dict:
33+
def _format_table(self, table_name: str, comment: str | None = None) -> dict:
3434
return {
3535
"name": table_name,
3636
"comment": comment or "",
@@ -48,7 +48,7 @@ class ColumnsInspector(BaseInspector, DiffMixin):
4848

4949
key = "columns"
5050

51-
def inspect(self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None) -> dict:
51+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
5252
ignore_clauses = self._filter_ignorers(ignore_specs)
5353

5454
inspector = self._get_inspector(engine)
@@ -90,7 +90,7 @@ class PrimaryKeysInspector(BaseInspector, DiffMixin):
9090

9191
key = "primary_keys"
9292

93-
def inspect(self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None) -> dict:
93+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
9494
ignore_clauses = self._filter_ignorers(ignore_specs)
9595

9696
inspector = self._get_inspector(engine)
@@ -121,7 +121,7 @@ class ForeignKeysInspector(BaseInspector, DiffMixin):
121121

122122
key = "foreign_keys"
123123

124-
def inspect(self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None) -> dict:
124+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
125125
ignore_clauses = self._filter_ignorers(ignore_specs)
126126

127127
inspector = self._get_inspector(engine)
@@ -155,7 +155,7 @@ class IndexesInspector(BaseInspector, DiffMixin):
155155

156156
key = "indexes"
157157

158-
def inspect(self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None) -> dict:
158+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
159159
ignore_clauses = self._filter_ignorers(ignore_specs)
160160
inspector = self._get_inspector(engine)
161161
table_names = inspector.get_table_names()
@@ -183,7 +183,7 @@ class UniqueConstraintsInspector(BaseInspector, DiffMixin):
183183

184184
key = "unique_constraints"
185185

186-
def inspect(self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None) -> dict:
186+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
187187
ignore_clauses = self._filter_ignorers(ignore_specs)
188188
inspector = self._get_inspector(engine)
189189
table_names = inspector.get_table_names()
@@ -220,7 +220,7 @@ class CheckConstraintsInspector(BaseInspector, DiffMixin):
220220

221221
key = "check_constraints"
222222

223-
def inspect(self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None) -> dict:
223+
def inspect(self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None) -> dict:
224224
ignore_clauses = self._filter_ignorers(ignore_specs)
225225
inspector = self._get_inspector(engine)
226226
table_names = inspector.get_table_names()
@@ -250,7 +250,7 @@ class EnumsInspector(BaseInspector, DiffMixin):
250250
db_level = True
251251

252252
def inspect(
253-
self, engine: Engine, ignore_specs: Optional[list[IgnoreSpecType]] = None
253+
self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None
254254
) -> list[dict]:
255255
inspector = self._get_inspector(engine)
256256

tests/models/models_one.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# ty: ignore[invalid-assignment]
22
import datetime
33
import enum
4-
from typing import Optional
54

65
from sqlalchemy import (
76
CheckConstraint,
@@ -36,8 +35,8 @@ class Employee(Base):
3635
age: Mapped[int] = Column(Integer, nullable=False, default=21)
3736
ssn: Mapped[str] = Column(Unicode(30), nullable=False)
3837
number_of_pets: Mapped[int] = Column(Integer, default=1, nullable=False)
39-
title: Mapped[Optional[Title]] = Column(Enum(Title, native_enum=True))
40-
department: Mapped[Optional[str]] = Column( # ty: ignore[invalid-assignment]
38+
title: Mapped[Title | None] = Column(Enum(Title, native_enum=True))
39+
department: Mapped[str | None] = Column( # ty: ignore[invalid-assignment]
4140
Enum("Product", "Engineering", "Sales", native_enum=False)
4241
)
4342

@@ -77,7 +76,7 @@ class Role(Base):
7776

7877
employees: Mapped[list["Employee"]] = relationship("Employee", back_populates="role")
7978

80-
role_type: Mapped[Optional[str]] = Column(Enum("Permanent", "Contractor", name="role_type"))
79+
role_type: Mapped[str | None] = Column(Enum("Permanent", "Contractor", name="role_type"))
8180

8281

8382
class Skill(Base):
@@ -87,7 +86,7 @@ class Skill(Base):
8786
}
8887

8988
slug: Mapped[str] = Column(String(50), primary_key=True)
90-
description: Mapped[Optional[str]] = Column(Unicode(100), nullable=True)
89+
description: Mapped[str | None] = Column(Unicode(100), nullable=True)
9190

9291
employee: Mapped[int] = Column(
9392
Integer, ForeignKey("employees.id", name="fk_skills_employees"), nullable=False

0 commit comments

Comments
 (0)