Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI/CD


on:
push


jobs:

lint:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.11"
- "3.12"
- "3.13"
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python & uv
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install the project
run: uv sync --locked --all-extras --dev

- name: Run ruff check
run: uv run ruff check

- name: Run ruff format
run: uv run ruff format --check


test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.11"
- "3.12"
- "3.13"
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python & uv
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install the project
run: uv sync --locked --all-extras --dev

- name: Testing code with pytest
run: uv run pytest -v
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,10 @@ class Model(Base):
### String enums (VARCHAR)

This annotation will not create a native enum
The column will be `VARCHAR` (without length limit) so that you can easily update your enum
The column in the database will be `VARCHAR` (without length limit) so that you can easily update
your enum in your python code

* `BooleanColumn` – `BOOLEAN`
* `BooleanDefaultFalseColumn` – `BOOLEAN` with `default=False`
* `BooleanDefaultTrueColumn` – `BOOLEAN` with `default=True`
* `StrEnumColumn` – `VARCHAR`

```python
from enum import StrEnum
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ maintainers = [
]
description = "Table column annotations for SQLAlchemy"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.11"
license = "MIT"
license-files = ["LICENSE"]
classifiers = [
Expand All @@ -18,7 +18,6 @@ classifiers = [
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand Down
Empty file added tests/__init__.py
Empty file.
Empty file added tests/conftest.py
Empty file.
18 changes: 18 additions & 0 deletions tests/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, declared_attr


class DeclarativeBase:
"""Base class for models."""

@declared_attr
def __tablename__(cls) -> str: # noqa: N805
"""Return table name generated out of class name."""
return cls.__name__.lower()


Base = declarative_base(cls=DeclarativeBase)
engine = create_engine(
'sqlite:///:memory:',
echo=False,
)
55 changes: 55 additions & 0 deletions tests/test_booleans.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from sqlalchemy.orm import Mapped
from sqlalchemy.sql.ddl import CreateTable

from sqlalchemy_annotations import BooleanColumn, BooleanDefaultFalseColumn, BooleanDefaultTrueColumn, SerialPKColumn
from tests.db import Base, engine


def clean_multiline(sql: str) -> str:
"""Strip each line of a multiline string."""
lines = (line.strip() for line in sql.split('\n'))
return '\n'.join(line for line in lines if line != '')


def test_bool_not_null() -> None:
"""Check boolean columns not null."""

class B1(Base):
id: Mapped[SerialPKColumn]
bool_true: Mapped[BooleanDefaultTrueColumn]
bool_false: Mapped[BooleanDefaultFalseColumn]
bool_no_default: Mapped[BooleanColumn]

sql = CreateTable(B1.__table__).compile(engine)
expected = [
'CREATE TABLE b1 (',
'id INTEGER NOT NULL,',
'bool_true BOOLEAN NOT NULL,',
'bool_false BOOLEAN NOT NULL,',
'bool_no_default BOOLEAN NOT NULL,',
'PRIMARY KEY (id)',
')',
]
assert clean_multiline(str(sql)) == '\n'.join(expected)


def test_bool_with_null() -> None:
"""Check boolean columns with nulls."""

class B2(Base):
id: Mapped[SerialPKColumn]
bool_true: Mapped[BooleanDefaultTrueColumn | None]
bool_false: Mapped[BooleanDefaultFalseColumn | None]
bool_no_default: Mapped[BooleanColumn | None]

sql = CreateTable(B2.__table__).compile(engine)
expected = [
'CREATE TABLE b2 (',
'id INTEGER NOT NULL,',
'bool_true BOOLEAN,',
'bool_false BOOLEAN,',
'bool_no_default BOOLEAN,',
'PRIMARY KEY (id)',
')',
]
assert clean_multiline(str(sql)) == '\n'.join(expected)
403 changes: 157 additions & 246 deletions uv.lock

Large diffs are not rendered by default.