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.
Files changed (37) hide show
  1. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/workflows/publish.yml +2 -2
  2. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.pre-commit-config.yaml +2 -2
  3. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/CHANGES.rst +16 -0
  4. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/PKG-INFO +1 -1
  5. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/generators.py +35 -1
  6. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/PKG-INFO +1 -1
  7. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_generator_declarative.py +26 -0
  8. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_generator_tables.py +67 -1
  9. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/FUNDING.yml +0 -0
  10. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  11. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  12. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/ISSUE_TEMPLATE/features_request.yaml +0 -0
  13. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/dependabot.yml +0 -0
  14. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/pull_request_template.md +0 -0
  15. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.github/workflows/test.yml +0 -0
  16. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/.gitignore +0 -0
  17. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/CONTRIBUTING.rst +0 -0
  18. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/LICENSE +0 -0
  19. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/README.rst +0 -0
  20. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/pyproject.toml +0 -0
  21. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/setup.cfg +0 -0
  22. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/__init__.py +0 -0
  23. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/__main__.py +0 -0
  24. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/cli.py +0 -0
  25. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/models.py +0 -0
  26. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/py.typed +0 -0
  27. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen/utils.py +0 -0
  28. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/SOURCES.txt +0 -0
  29. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/dependency_links.txt +0 -0
  30. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/entry_points.txt +0 -0
  31. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/requires.txt +0 -0
  32. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/src/sqlacodegen.egg-info/top_level.txt +0 -0
  33. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/__init__.py +0 -0
  34. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/conftest.py +0 -0
  35. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_cli.py +0 -0
  36. {sqlacodegen-4.0.2 → sqlacodegen-4.0.4}/tests/test_generator_dataclass.py +0 -0
  37. {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@v6
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@v7
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.14.10
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.19.1
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlacodegen
3
- Version: 4.0.2
3
+ Version: 4.0.4
4
4
  Summary: Automatic model code generator for SQLAlchemy
5
5
  Author-email: Alex Grönholm <alex.gronholm@nextday.fi>
6
6
  Maintainer-email: Idan Sheinberg <ishinberg0@gmail.com>
@@ -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
- args.append(repr(column.server_default))
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(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlacodegen
3
- Version: 4.0.2
3
+ Version: 4.0.4
4
4
  Summary: Automatic model code generator for SQLAlchemy
5
5
  Author-email: Alex Grönholm <alex.gronholm@nextday.fi>
6
6
  Maintainer-email: Idan Sheinberg <ishinberg0@gmail.com>
@@ -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 ARRAY, Column, Double, Integer, MetaData, Table
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