sqlacodegen 4.0.2__tar.gz → 4.0.4__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.
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/workflows/publish.yml +2 -2
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.pre-commit-config.yaml +2 -2
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/CHANGES.rst +16 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/PKG-INFO +1 -1
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/generators.py +35 -1
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/PKG-INFO +1 -1
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_generator_declarative.py +26 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_generator_tables.py +67 -1
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/FUNDING.yml +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/ISSUE_TEMPLATE/features_request.yaml +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/dependabot.yml +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/pull_request_template.md +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/workflows/test.yml +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.gitignore +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/CONTRIBUTING.rst +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/LICENSE +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/README.rst +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/pyproject.toml +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/setup.cfg +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/__init__.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/__main__.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/cli.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/models.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/py.typed +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/utils.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/SOURCES.txt +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/dependency_links.txt +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/entry_points.txt +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/requires.txt +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/top_level.txt +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/__init__.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/conftest.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_cli.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_generator_dataclass.py +0 -0
- {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_generator_sqlmodel.py +0 -0
|
@@ -24,7 +24,7 @@ jobs:
|
|
|
24
24
|
- name: Create packages
|
|
25
25
|
run: python -m build
|
|
26
26
|
- name: Archive packages
|
|
27
|
-
uses: actions/upload-artifact@
|
|
27
|
+
uses: actions/upload-artifact@v7
|
|
28
28
|
with:
|
|
29
29
|
name: dist
|
|
30
30
|
path: dist
|
|
@@ -38,7 +38,7 @@ jobs:
|
|
|
38
38
|
id-token: write
|
|
39
39
|
steps:
|
|
40
40
|
- name: Retrieve packages
|
|
41
|
-
uses: actions/download-artifact@
|
|
41
|
+
uses: actions/download-artifact@v8
|
|
42
42
|
with:
|
|
43
43
|
name: dist
|
|
44
44
|
path: dist
|
|
@@ -16,14 +16,14 @@ repos:
|
|
|
16
16
|
- id: trailing-whitespace
|
|
17
17
|
|
|
18
18
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
19
|
-
rev: v0.
|
|
19
|
+
rev: v0.15.9
|
|
20
20
|
hooks:
|
|
21
21
|
- id: ruff
|
|
22
22
|
args: [--fix, --show-fixes]
|
|
23
23
|
- id: ruff-format
|
|
24
24
|
|
|
25
25
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
26
|
-
rev: v1.
|
|
26
|
+
rev: v1.20.0
|
|
27
27
|
hooks:
|
|
28
28
|
- id: mypy
|
|
29
29
|
additional_dependencies:
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
Version history
|
|
2
2
|
===============
|
|
3
3
|
|
|
4
|
+
**4.0.4*
|
|
5
|
+
|
|
6
|
+
- Added autoincrement to primary key columns to prevent missing field errors.
|
|
7
|
+
(`#473 <https://github.com/agronholm/sqlacodegen/issues/473>`_; PR by @jtmonroe)
|
|
8
|
+
- Preserve dialect-specific ``ARRAY`` types (e.g. ``postgresql.ARRAY``) instead
|
|
9
|
+
of adapting them to the generic ``sqlalchemy.ARRAY``. The generic type does
|
|
10
|
+
not implement operators like ``.contains()``, so adapting silently broke
|
|
11
|
+
PostgreSQL array queries on generated models.
|
|
12
|
+
(`#441 <https://github.com/agronholm/sqlacodegen/issues/441>`_)
|
|
13
|
+
|
|
14
|
+
**4.0.3**
|
|
15
|
+
|
|
16
|
+
- Improved rendering of ``Identity`` server defaults by explicitly rendering
|
|
17
|
+
non-default parameters; ``Decimal`` values (as returned by some databases) are
|
|
18
|
+
now cast to ``int`` (PR by @NotCarlosSerrano)
|
|
19
|
+
|
|
4
20
|
**4.0.2**
|
|
5
21
|
|
|
6
22
|
- Fixed rendering of inherited keyword arguments for dialect-specific types that use
|
|
@@ -7,6 +7,7 @@ from abc import ABCMeta, abstractmethod
|
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from collections.abc import Collection, Iterable, Mapping, Sequence
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
+
from decimal import Decimal
|
|
10
11
|
from importlib import import_module
|
|
11
12
|
from inspect import Parameter
|
|
12
13
|
from itertools import count
|
|
@@ -470,6 +471,9 @@ class TablesGenerator(CodeGenerator):
|
|
|
470
471
|
)
|
|
471
472
|
or column.primary_key
|
|
472
473
|
)
|
|
474
|
+
is_autoincrement = (
|
|
475
|
+
isinstance(column.autoincrement, bool) and column.autoincrement
|
|
476
|
+
)
|
|
473
477
|
has_index = any(
|
|
474
478
|
set(i.columns) == {column} and uses_default_name(i)
|
|
475
479
|
for i in column.table.indexes
|
|
@@ -493,6 +497,8 @@ class TablesGenerator(CodeGenerator):
|
|
|
493
497
|
kwargs["key"] = column.key
|
|
494
498
|
if is_primary:
|
|
495
499
|
kwargs["primary_key"] = True
|
|
500
|
+
if is_autoincrement and is_primary:
|
|
501
|
+
kwargs["autoincrement"] = True
|
|
496
502
|
if not column.nullable and not column.primary_key:
|
|
497
503
|
kwargs["nullable"] = False
|
|
498
504
|
if column.nullable and is_part_of_composite_pk:
|
|
@@ -521,7 +527,29 @@ class TablesGenerator(CodeGenerator):
|
|
|
521
527
|
render_callable("Computed", repr(expression), kwargs=computed_kwargs)
|
|
522
528
|
)
|
|
523
529
|
elif isinstance(column.server_default, Identity):
|
|
524
|
-
|
|
530
|
+
identity = column.server_default
|
|
531
|
+
identity_kwargs: dict[str, Any] = {}
|
|
532
|
+
|
|
533
|
+
for name, param in inspect.signature(Identity).parameters.items():
|
|
534
|
+
if name == "self" or param.kind in (
|
|
535
|
+
Parameter.VAR_POSITIONAL,
|
|
536
|
+
Parameter.VAR_KEYWORD,
|
|
537
|
+
):
|
|
538
|
+
continue
|
|
539
|
+
|
|
540
|
+
value = getattr(identity, name, None)
|
|
541
|
+
if value is None:
|
|
542
|
+
continue
|
|
543
|
+
|
|
544
|
+
if isinstance(value, Decimal):
|
|
545
|
+
value = int(value)
|
|
546
|
+
|
|
547
|
+
if param.default is not Parameter.empty and value == param.default:
|
|
548
|
+
continue
|
|
549
|
+
|
|
550
|
+
identity_kwargs[name] = value
|
|
551
|
+
|
|
552
|
+
args.append(render_callable("Identity", kwargs=identity_kwargs))
|
|
525
553
|
elif column.server_default:
|
|
526
554
|
kwargs["server_default"] = repr(column.server_default)
|
|
527
555
|
|
|
@@ -1008,6 +1036,12 @@ class TablesGenerator(CodeGenerator):
|
|
|
1008
1036
|
column.server_default = None
|
|
1009
1037
|
|
|
1010
1038
|
def get_adapted_type(self, coltype: Any) -> Any:
|
|
1039
|
+
# Keep dialect-specific ARRAY subclasses; the generic sqlalchemy.ARRAY
|
|
1040
|
+
# is missing operators like .contains() (GH-441).
|
|
1041
|
+
if isinstance(coltype, ARRAY) and type(coltype) is not ARRAY:
|
|
1042
|
+
coltype.item_type = self.get_adapted_type(coltype.item_type)
|
|
1043
|
+
return coltype
|
|
1044
|
+
|
|
1011
1045
|
compiled_type = coltype.compile(self.bind.engine.dialect)
|
|
1012
1046
|
for supercls in coltype.__class__.__mro__:
|
|
1013
1047
|
if not supercls.__name__.startswith("_") and hasattr(
|
|
@@ -1497,6 +1497,32 @@ class SimpleItems(Base):
|
|
|
1497
1497
|
)
|
|
1498
1498
|
|
|
1499
1499
|
|
|
1500
|
+
def test_composite_autoincrement_pk(generator: CodeGenerator) -> None:
|
|
1501
|
+
Table(
|
|
1502
|
+
"simple_autoincrement_items",
|
|
1503
|
+
generator.metadata,
|
|
1504
|
+
Column("id1", INTEGER, primary_key=True, autoincrement=True),
|
|
1505
|
+
Column("id2", INTEGER, primary_key=True),
|
|
1506
|
+
)
|
|
1507
|
+
validate_code(
|
|
1508
|
+
generator.generate(),
|
|
1509
|
+
"""\
|
|
1510
|
+
from sqlalchemy import Integer
|
|
1511
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
1512
|
+
|
|
1513
|
+
class Base(DeclarativeBase):
|
|
1514
|
+
pass
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
class SimpleAutoincrementItems(Base):
|
|
1518
|
+
__tablename__ = 'simple_autoincrement_items'
|
|
1519
|
+
|
|
1520
|
+
id1: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
1521
|
+
id2: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
1522
|
+
""",
|
|
1523
|
+
)
|
|
1524
|
+
|
|
1525
|
+
|
|
1500
1526
|
def test_joined_inheritance(generator: CodeGenerator) -> None:
|
|
1501
1527
|
Table(
|
|
1502
1528
|
"simple_sub_items",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from decimal import Decimal
|
|
3
4
|
from textwrap import dedent
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
@@ -129,7 +130,8 @@ def test_arrays(generator: CodeGenerator) -> None:
|
|
|
129
130
|
validate_code(
|
|
130
131
|
generator.generate(),
|
|
131
132
|
"""\
|
|
132
|
-
from sqlalchemy import
|
|
133
|
+
from sqlalchemy import Column, Double, Integer, MetaData, Table
|
|
134
|
+
from sqlalchemy.dialects.postgresql import ARRAY
|
|
133
135
|
|
|
134
136
|
metadata = MetaData()
|
|
135
137
|
|
|
@@ -143,6 +145,36 @@ def test_arrays(generator: CodeGenerator) -> None:
|
|
|
143
145
|
)
|
|
144
146
|
|
|
145
147
|
|
|
148
|
+
@pytest.mark.parametrize("engine", ["postgresql"], indirect=["engine"])
|
|
149
|
+
def test_array_preserves_dialect_for_runtime_operators(
|
|
150
|
+
generator: CodeGenerator,
|
|
151
|
+
) -> None:
|
|
152
|
+
"""Regression test for GH-441."""
|
|
153
|
+
Table(
|
|
154
|
+
"simple_items",
|
|
155
|
+
generator.metadata,
|
|
156
|
+
Column("id", postgresql.TEXT, primary_key=True),
|
|
157
|
+
Column("tags", postgresql.ARRAY(postgresql.TEXT)),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
validate_code(
|
|
161
|
+
generator.generate(),
|
|
162
|
+
"""\
|
|
163
|
+
from sqlalchemy import Column, MetaData, Table, Text
|
|
164
|
+
from sqlalchemy.dialects.postgresql import ARRAY
|
|
165
|
+
|
|
166
|
+
metadata = MetaData()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
t_simple_items = Table(
|
|
170
|
+
'simple_items', metadata,
|
|
171
|
+
Column('id', Text, primary_key=True),
|
|
172
|
+
Column('tags', ARRAY(Text()))
|
|
173
|
+
)
|
|
174
|
+
""",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
146
178
|
@pytest.mark.parametrize("engine", ["postgresql"], indirect=["engine"])
|
|
147
179
|
def test_jsonb(generator: CodeGenerator) -> None:
|
|
148
180
|
Table(
|
|
@@ -1217,6 +1249,40 @@ def test_identity_column(generator: CodeGenerator) -> None:
|
|
|
1217
1249
|
)
|
|
1218
1250
|
|
|
1219
1251
|
|
|
1252
|
+
def test_identity_column_decimal_values(generator: CodeGenerator) -> None:
|
|
1253
|
+
# MSSQL reflects Identity column parameters (start, increment) as Decimal
|
|
1254
|
+
# values instead of integers. This test ensures those are serialized correctly.
|
|
1255
|
+
identity = Identity(start=1, increment=2)
|
|
1256
|
+
# Simulate database reflection returning Decimal values (as MSSQL does)
|
|
1257
|
+
identity.start = Decimal("1") # type: ignore[assignment]
|
|
1258
|
+
identity.increment = Decimal("2") # type: ignore[assignment]
|
|
1259
|
+
Table(
|
|
1260
|
+
"simple_items",
|
|
1261
|
+
generator.metadata,
|
|
1262
|
+
Column(
|
|
1263
|
+
"id",
|
|
1264
|
+
INTEGER,
|
|
1265
|
+
primary_key=True,
|
|
1266
|
+
server_default=identity,
|
|
1267
|
+
),
|
|
1268
|
+
)
|
|
1269
|
+
|
|
1270
|
+
validate_code(
|
|
1271
|
+
generator.generate(),
|
|
1272
|
+
"""\
|
|
1273
|
+
from sqlalchemy import Column, Identity, Integer, MetaData, Table
|
|
1274
|
+
|
|
1275
|
+
metadata = MetaData()
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
t_simple_items = Table(
|
|
1279
|
+
'simple_items', metadata,
|
|
1280
|
+
Column('id', Integer, Identity(start=1, increment=2), primary_key=True)
|
|
1281
|
+
)
|
|
1282
|
+
""",
|
|
1283
|
+
)
|
|
1284
|
+
|
|
1285
|
+
|
|
1220
1286
|
def test_multiline_column_comment(generator: CodeGenerator) -> None:
|
|
1221
1287
|
Table(
|
|
1222
1288
|
"simple_items",
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|