sqlacodegen
Advanced tools
| # Keep GitHub Actions up to date with GitHub's Dependabot... | ||
| # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot | ||
| # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem | ||
| version: 2 | ||
| updates: | ||
| - package-ecosystem: github-actions | ||
| directory: / | ||
| groups: | ||
| github-actions: | ||
| patterns: | ||
| - "*" # Group all Actions updates into a single larger pull request | ||
| schedule: | ||
| interval: quarterly |
| tidelift: pypi/sqlacodegen |
@@ -17,5 +17,5 @@ name: Publish packages to PyPI | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/checkout@v6 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
@@ -28,3 +28,3 @@ python-version: 3.x | ||
| - name: Archive packages | ||
| uses: actions/upload-artifact@v4 | ||
| uses: actions/upload-artifact@v6 | ||
| with: | ||
@@ -43,3 +43,6 @@ name: dist | ||
| - name: Retrieve packages | ||
| uses: actions/download-artifact@v4 | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: dist | ||
| path: dist | ||
| - name: Upload packages | ||
@@ -55,3 +58,3 @@ uses: pypa/gh-action-pypi-publish@release/v1 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/checkout@v6 | ||
| - id: changelog | ||
@@ -58,0 +61,0 @@ uses: agronholm/release-notes@v1 |
@@ -16,5 +16,5 @@ name: test suite | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/checkout@v6 | ||
| - name: Set up Python ${{ matrix.python-version }} | ||
| uses: actions/setup-python@v5 | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
@@ -26,3 +26,3 @@ python-version: ${{ matrix.python-version }} | ||
| - name: Install dependencies | ||
| run: pip install -e .[test] | ||
| run: pip install --group test -e .[sqlmodel,citext,geoalchemy2,pgvector] | ||
| - name: Test with pytest | ||
@@ -29,0 +29,0 @@ run: coverage run -m pytest |
@@ -19,3 +19,3 @@ # This is the configuration file for pre-commit (https://pre-commit.com/). | ||
| - repo: https://github.com/astral-sh/ruff-pre-commit | ||
| rev: v0.13.3 | ||
| rev: v0.14.10 | ||
| hooks: | ||
@@ -27,3 +27,3 @@ - id: ruff | ||
| - repo: https://github.com/pre-commit/mirrors-mypy | ||
| rev: v1.18.2 | ||
| rev: v1.19.1 | ||
| hooks: | ||
@@ -30,0 +30,0 @@ - id: mypy |
+11
-0
| Version history | ||
| =============== | ||
| **4.0.0rc1** | ||
| - **BACKWARD INCOMPATIBLE** ``TablesGenerator.render_column_type()`` was changed to | ||
| receive the ``Column`` object instead of the column type object as its sole argument | ||
| - Added Python enum generation for native database ENUM types (e.g., PostgreSQL / MySQL ENUM). | ||
| Retained synthetic Python enum generation from CHECK constraints with | ||
| IN clauses (e.g., ``column IN ('val1', 'val2', ...)``). Use ``--options nonativeenums`` to | ||
| disable enum generation for native database enums. Use ``--options nosyntheticenums`` to | ||
| disable enum generation for synthetic database enums (VARCHAR columns with check constraints). | ||
| (PR by @sheinbergon) | ||
| **3.2.0** | ||
@@ -5,0 +16,0 @@ |
+14
-7
| Metadata-Version: 2.4 | ||
| Name: sqlacodegen | ||
| Version: 3.2.0 | ||
| Version: 4.0.0rc1 | ||
| Summary: Automatic model code generator for SQLAlchemy | ||
@@ -28,8 +28,2 @@ Author-email: Alex Grönholm <alex.gronholm@nextday.fi> | ||
| Requires-Dist: inflect>=4.0.0 | ||
| Provides-Extra: test | ||
| Requires-Dist: sqlacodegen[geoalchemy2,pgvector,sqlmodel]; extra == "test" | ||
| Requires-Dist: pytest>=7.4; extra == "test" | ||
| Requires-Dist: coverage>=7; extra == "test" | ||
| Requires-Dist: psycopg[binary]; extra == "test" | ||
| Requires-Dist: mysql-connector-python; extra == "test" | ||
| Provides-Extra: sqlmodel | ||
@@ -51,2 +45,5 @@ Requires-Dist: sqlmodel>=0.0.22; extra == "sqlmodel" | ||
| :alt: Code Coverage | ||
| .. image:: https://tidelift.com/badges/package/pypi/sqlacodegen | ||
| :target: https://tidelift.com/subscription/pkg/pypi-sqlacodegen | ||
| :alt: Tidelift | ||
@@ -151,2 +148,4 @@ This is a tool that reads the structure of an existing database and generates the | ||
| * ``noindexes``: ignore indexes | ||
| * ``nonativeenums``: don't generate Python enum classes for native database ENUM types (e.g., PostgreSQL ENUM); use plain string mapping instead | ||
| * ``nosyntheticenums``: don't generate Python enum classes from CHECK constraints with IN clauses (e.g., ``column IN ('value1', 'value2', ...)``); preserves CHECK constraints as-is | ||
| * ``noidsuffix``: prevent the special naming logic for single column many-to-one | ||
@@ -257,1 +256,9 @@ and one-to-one relationships (see `Relationship naming logic`_ for details) | ||
| .. _sqlalchemy: https://app.gitter.im/#/room/#sqlalchemy_community:gitter.im | ||
| Security contact information | ||
| ============================ | ||
| To report a security vulnerability, please use the `Tidelift security contact`_. | ||
| Tidelift will coordinate the fix and disclosure. | ||
| .. _Tidelift security contact: https://tidelift.com/security |
+10
-8
@@ -42,9 +42,2 @@ [build-system] | ||
| [project.optional-dependencies] | ||
| test = [ | ||
| "sqlacodegen[sqlmodel,pgvector,geoalchemy2]", | ||
| "pytest >= 7.4", | ||
| "coverage >= 7", | ||
| "psycopg[binary]", | ||
| "mysql-connector-python", | ||
| ] | ||
| sqlmodel = ["sqlmodel >= 0.0.22"] | ||
@@ -64,2 +57,10 @@ citext = ["sqlalchemy-citext >= 1.7.0"] | ||
| [dependency-groups] | ||
| test = [ | ||
| "pytest >= 7.4", | ||
| "coverage >= 7", | ||
| "psycopg[binary]", | ||
| "mysql-connector-python", | ||
| ] | ||
| [tool.setuptools_scm] | ||
@@ -104,2 +105,3 @@ version_scheme = "post-release" | ||
| commands = [["python", "-m", "pytest", { replace = "posargs", extend = true }]] | ||
| extras = ["test"] | ||
| dependency_groups = ["test"] | ||
| extras = ["sqlmodel", "citext", "geoalchemy2", "pgvector"] |
+13
-0
@@ -7,2 +7,5 @@ .. image:: https://github.com/agronholm/sqlacodegen/actions/workflows/test.yml/badge.svg | ||
| :alt: Code Coverage | ||
| .. image:: https://tidelift.com/badges/package/pypi/sqlacodegen | ||
| :target: https://tidelift.com/subscription/pkg/pypi-sqlacodegen | ||
| :alt: Tidelift | ||
@@ -107,2 +110,4 @@ This is a tool that reads the structure of an existing database and generates the | ||
| * ``noindexes``: ignore indexes | ||
| * ``nonativeenums``: don't generate Python enum classes for native database ENUM types (e.g., PostgreSQL ENUM); use plain string mapping instead | ||
| * ``nosyntheticenums``: don't generate Python enum classes from CHECK constraints with IN clauses (e.g., ``column IN ('value1', 'value2', ...)``); preserves CHECK constraints as-is | ||
| * ``noidsuffix``: prevent the special naming logic for single column many-to-one | ||
@@ -213,1 +218,9 @@ and one-to-one relationships (see `Relationship naming logic`_ for details) | ||
| .. _sqlalchemy: https://app.gitter.im/#/room/#sqlalchemy_community:gitter.im | ||
| Security contact information | ||
| ============================ | ||
| To report a security vulnerability, please use the `Tidelift security contact`_. | ||
| Tidelift will coordinate the fix and disclosure. | ||
| .. _Tidelift security contact: https://tidelift.com/security |
| Metadata-Version: 2.4 | ||
| Name: sqlacodegen | ||
| Version: 3.2.0 | ||
| Version: 4.0.0rc1 | ||
| Summary: Automatic model code generator for SQLAlchemy | ||
@@ -28,8 +28,2 @@ Author-email: Alex Grönholm <alex.gronholm@nextday.fi> | ||
| Requires-Dist: inflect>=4.0.0 | ||
| Provides-Extra: test | ||
| Requires-Dist: sqlacodegen[geoalchemy2,pgvector,sqlmodel]; extra == "test" | ||
| Requires-Dist: pytest>=7.4; extra == "test" | ||
| Requires-Dist: coverage>=7; extra == "test" | ||
| Requires-Dist: psycopg[binary]; extra == "test" | ||
| Requires-Dist: mysql-connector-python; extra == "test" | ||
| Provides-Extra: sqlmodel | ||
@@ -51,2 +45,5 @@ Requires-Dist: sqlmodel>=0.0.22; extra == "sqlmodel" | ||
| :alt: Code Coverage | ||
| .. image:: https://tidelift.com/badges/package/pypi/sqlacodegen | ||
| :target: https://tidelift.com/subscription/pkg/pypi-sqlacodegen | ||
| :alt: Tidelift | ||
@@ -151,2 +148,4 @@ This is a tool that reads the structure of an existing database and generates the | ||
| * ``noindexes``: ignore indexes | ||
| * ``nonativeenums``: don't generate Python enum classes for native database ENUM types (e.g., PostgreSQL ENUM); use plain string mapping instead | ||
| * ``nosyntheticenums``: don't generate Python enum classes from CHECK constraints with IN clauses (e.g., ``column IN ('value1', 'value2', ...)``); preserves CHECK constraints as-is | ||
| * ``noidsuffix``: prevent the special naming logic for single column many-to-one | ||
@@ -257,1 +256,9 @@ and one-to-one relationships (see `Relationship naming logic`_ for details) | ||
| .. _sqlalchemy: https://app.gitter.im/#/room/#sqlalchemy_community:gitter.im | ||
| Security contact information | ||
| ============================ | ||
| To report a security vulnerability, please use the `Tidelift security contact`_. | ||
| Tidelift will coordinate the fix and disclosure. | ||
| .. _Tidelift security contact: https://tidelift.com/security |
@@ -15,8 +15,1 @@ SQLAlchemy>=2.0.29 | ||
| sqlmodel>=0.0.22 | ||
| [test] | ||
| sqlacodegen[geoalchemy2,pgvector,sqlmodel] | ||
| pytest>=7.4 | ||
| coverage>=7 | ||
| psycopg[binary] | ||
| mysql-connector-python |
@@ -8,2 +8,4 @@ .gitignore | ||
| pyproject.toml | ||
| .github/FUNDING.yml | ||
| .github/dependabot.yml | ||
| .github/pull_request_template.md | ||
@@ -10,0 +12,0 @@ .github/ISSUE_TEMPLATE/bug_report.yaml |
@@ -213,8 +213,2 @@ from __future__ import annotations | ||
| def get_stdlib_module_names() -> set[str]: | ||
| major, minor = sys.version_info.major, sys.version_info.minor | ||
| if (major, minor) > (3, 9): | ||
| return set(sys.builtin_module_names) | set(sys.stdlib_module_names) | ||
| else: | ||
| from stdlib_list import stdlib_list | ||
| return set(sys.builtin_module_names) | set(stdlib_list(f"{major}.{minor}")) | ||
| return set(sys.builtin_module_names) | set(sys.stdlib_module_names) |
@@ -207,1 +207,66 @@ from __future__ import annotations | ||
| ) | ||
| def test_check_constraint_not_converted_to_enum(generator: CodeGenerator) -> None: | ||
| Table( | ||
| "users", | ||
| generator.metadata, | ||
| Column("id", INTEGER, primary_key=True), | ||
| Column("status", VARCHAR(20), nullable=False), | ||
| CheckConstraint("users.status IN ('active', 'inactive', 'pending')"), | ||
| ) | ||
| # Recreate generator with nosyntheticenums option to preserve constraints | ||
| generator = SQLModelGenerator( | ||
| generator.metadata, generator.bind, ["nosyntheticenums"] | ||
| ) | ||
| validate_code( | ||
| generator.generate(), | ||
| """\ | ||
| from sqlalchemy import CheckConstraint, Column, Integer, String | ||
| from sqlmodel import Field, SQLModel | ||
| class Users(SQLModel, table=True): | ||
| __table_args__ = ( | ||
| CheckConstraint("users.status IN ('active', 'inactive', 'pending')"), | ||
| ) | ||
| id: int = Field(sa_column=Column('id', Integer, primary_key=True)) | ||
| status: str = Field(sa_column=Column('status', String(20), nullable=False)) | ||
| """, | ||
| ) | ||
| def test_synthetic_enum_generation(generator: CodeGenerator) -> None: | ||
| Table( | ||
| "accounts", | ||
| generator.metadata, | ||
| Column("id", INTEGER, primary_key=True), | ||
| Column("status", VARCHAR(20), nullable=False), | ||
| CheckConstraint("accounts.status IN ('active', 'inactive', 'pending')"), | ||
| ) | ||
| validate_code( | ||
| generator.generate(), | ||
| """\ | ||
| import enum | ||
| from sqlalchemy import CheckConstraint, Column, Enum, Integer | ||
| from sqlmodel import Field, SQLModel | ||
| class AccountsStatus(str, enum.Enum): | ||
| ACTIVE = 'active' | ||
| INACTIVE = 'inactive' | ||
| PENDING = 'pending' | ||
| class Accounts(SQLModel, table=True): | ||
| __table_args__ = ( | ||
| CheckConstraint("accounts.status IN ('active', 'inactive', 'pending')"), | ||
| ) | ||
| id: int = Field(sa_column=Column('id', Integer, primary_key=True)) | ||
| status: AccountsStatus = Field(sa_column=Column('status', Enum(AccountsStatus), nullable=False)) | ||
| """, | ||
| ) |
@@ -63,2 +63,3 @@ from __future__ import annotations | ||
| from tests.test_generator_tables import TIMESTAMP_DECORATOR | ||
| import enum | ||
@@ -71,5 +72,10 @@ from pgvector.sqlalchemy.vector import VECTOR | ||
| class Blah(str, enum.Enum): | ||
| A = 'A' | ||
| B = 'B' | ||
| t_simple_items = Table( | ||
| 'simple_items', metadata, | ||
| Column('enum', Enum('A', 'B', name='blah', schema='someschema')), | ||
| Column('enum', Enum(Blah)), | ||
| Column('bool', Boolean), | ||
@@ -207,3 +213,3 @@ Column('vector', VECTOR(3)), | ||
| def test_enum_detection(generator: CodeGenerator) -> None: | ||
| def test_check_constraint_preserved(generator: CodeGenerator) -> None: | ||
| Table( | ||
@@ -216,6 +222,11 @@ "simple_items", | ||
| # Recreate generator with nosyntheticenums option to preserve constraints | ||
| generator = TablesGenerator( | ||
| generator.metadata, generator.bind, ["nosyntheticenums"] | ||
| ) | ||
| validate_code( | ||
| generator.generate(), | ||
| """\ | ||
| from sqlalchemy import Column, Enum, MetaData, Table | ||
| from sqlalchemy import CheckConstraint, Column, MetaData, String, Table | ||
@@ -227,3 +238,4 @@ metadata = MetaData() | ||
| 'simple_items', metadata, | ||
| Column('enum', Enum('A', "\\\\'B", 'C')) | ||
| Column('enum', String(255)), | ||
| CheckConstraint("simple_items.enum IN ('A', '\\\\'B', 'C')") | ||
| ) | ||
@@ -234,2 +246,84 @@ """, | ||
| def test_synthetic_enum_generation(generator: CodeGenerator) -> None: | ||
| Table( | ||
| "simple_items", | ||
| generator.metadata, | ||
| Column("id", INTEGER, primary_key=True), | ||
| Column("status", VARCHAR(20)), | ||
| CheckConstraint("simple_items.status IN ('active', 'inactive', 'pending')"), | ||
| ) | ||
| validate_code( | ||
| generator.generate(), | ||
| """\ | ||
| import enum | ||
| from sqlalchemy import CheckConstraint, Column, Enum, Integer, MetaData, Table | ||
| metadata = MetaData() | ||
| class SimpleItemsStatus(str, enum.Enum): | ||
| ACTIVE = 'active' | ||
| INACTIVE = 'inactive' | ||
| PENDING = 'pending' | ||
| t_simple_items = Table( | ||
| 'simple_items', metadata, | ||
| Column('id', Integer, primary_key=True), | ||
| Column('status', Enum(SimpleItemsStatus)), | ||
| CheckConstraint("simple_items.status IN ('active', 'inactive', 'pending')") | ||
| ) | ||
| """, | ||
| ) | ||
| def test_enum_shared_values(generator: CodeGenerator) -> None: | ||
| from sqlalchemy import Enum as SAEnum | ||
| Table( | ||
| "users", | ||
| generator.metadata, | ||
| Column("id", INTEGER, primary_key=True), | ||
| Column("status", SAEnum("active", "inactive", "pending", name="status_enum")), | ||
| ) | ||
| Table( | ||
| "accounts", | ||
| generator.metadata, | ||
| Column("id", INTEGER, primary_key=True), | ||
| Column("status", SAEnum("active", "inactive", "pending", name="status_enum")), | ||
| ) | ||
| validate_code( | ||
| generator.generate(), | ||
| """\ | ||
| import enum | ||
| from sqlalchemy import Column, Enum, Integer, MetaData, Table | ||
| metadata = MetaData() | ||
| class StatusEnum(str, enum.Enum): | ||
| ACTIVE = 'active' | ||
| INACTIVE = 'inactive' | ||
| PENDING = 'pending' | ||
| t_accounts = Table( | ||
| 'accounts', metadata, | ||
| Column('id', Integer, primary_key=True), | ||
| Column('status', Enum(StatusEnum)) | ||
| ) | ||
| t_users = Table( | ||
| 'users', metadata, | ||
| Column('id', Integer, primary_key=True), | ||
| Column('status', Enum(StatusEnum)) | ||
| ) | ||
| """, | ||
| ) | ||
| @pytest.mark.parametrize("engine", ["postgresql"], indirect=["engine"]) | ||
@@ -236,0 +330,0 @@ def test_domain_text(generator: CodeGenerator) -> None: |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
260569
10.35%37
5.71%5277
11.4%