Skip to content

Commit

Permalink
Bump SQLAlchemy to 2.0.36 (home-assistant#126683)
Browse files Browse the repository at this point in the history
* Bump SQLAlchemy to 2.0.35

changelog: https://docs.sqlalchemy.org/en/20/changelog/changelog_20.html#change-2.0.35

* fix mocking

* adjust to .36

* remove ignored as these are now typed

* fix SQLAlchemy
  • Loading branch information
bdraco authored and balloob committed Dec 1, 2024
1 parent 06838c0 commit 4326689
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ jobs:
wheels-key: ${{ secrets.WHEELS_KEY }}
env-file: true
apk: "libffi-dev;openssl-dev;yaml-dev;nasm;zlib-dev"
skip-binary: aiohttp;multidict;yarl
skip-binary: aiohttp;multidict;yarl;SQLAlchemy
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements.txt"
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/recorder/db_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,14 @@ class Unused(CHAR):
"""An unused column type that behaves like a string."""


@compiles(UnusedDateTime, "mysql", "mariadb", "sqlite") # type: ignore[misc,no-untyped-call]
@compiles(Unused, "mysql", "mariadb", "sqlite") # type: ignore[misc,no-untyped-call]
@compiles(UnusedDateTime, "mysql", "mariadb", "sqlite")
@compiles(Unused, "mysql", "mariadb", "sqlite")
def compile_char_zero(type_: TypeDecorator, compiler: Any, **kw: Any) -> str:
"""Compile UnusedDateTime and Unused as CHAR(0) on mysql, mariadb, and sqlite."""
return "CHAR(0)" # Uses 1 byte on MySQL (no change on sqlite)


@compiles(Unused, "postgresql") # type: ignore[misc,no-untyped-call]
@compiles(Unused, "postgresql")
def compile_char_one(type_: TypeDecorator, compiler: Any, **kw: Any) -> str:
"""Compile Unused as CHAR(1) on postgresql."""
return "CHAR(1)" # Uses 1 byte
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/recorder/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"iot_class": "local_push",
"quality_scale": "internal",
"requirements": [
"SQLAlchemy==2.0.31",
"SQLAlchemy==2.0.36",
"fnv-hash-fast==1.0.2",
"psutil-home-assistant==0.0.1"
]
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/sql/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/sql",
"iot_class": "local_polling",
"requirements": ["SQLAlchemy==2.0.31", "sqlparse==0.5.0"]
"requirements": ["SQLAlchemy==2.0.36", "sqlparse==0.5.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/package_constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pyudev==0.24.1
PyYAML==6.0.2
requests==2.32.3
securetar==2024.11.0
SQLAlchemy==2.0.31
SQLAlchemy==2.0.36
standard-aifc==3.13.0;python_version>='3.13'
standard-telnetlib==3.13.0;python_version>='3.13'
typing-extensions>=4.12.2,<5.0
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ dependencies = [
"PyYAML==6.0.2",
"requests==2.32.3",
"securetar==2024.11.0",
"SQLAlchemy==2.0.31",
"SQLAlchemy==2.0.36",
"standard-aifc==3.13.0;python_version>='3.13'",
"standard-telnetlib==3.13.0;python_version>='3.13'",
"typing-extensions>=4.12.2,<5.0",
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ python-slugify==8.0.4
PyYAML==6.0.2
requests==2.32.3
securetar==2024.11.0
SQLAlchemy==2.0.31
SQLAlchemy==2.0.36
standard-aifc==3.13.0;python_version>='3.13'
standard-telnetlib==3.13.0;python_version>='3.13'
typing-extensions>=4.12.2,<5.0
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ RtmAPI==0.7.2

# homeassistant.components.recorder
# homeassistant.components.sql
SQLAlchemy==2.0.31
SQLAlchemy==2.0.36

# homeassistant.components.tami4
Tami4EdgeAPI==3.0
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ RtmAPI==0.7.2

# homeassistant.components.recorder
# homeassistant.components.sql
SQLAlchemy==2.0.31
SQLAlchemy==2.0.36

# homeassistant.components.tami4
Tami4EdgeAPI==3.0
Expand Down
52 changes: 23 additions & 29 deletions tests/components/sql/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

from sqlalchemy.exc import SQLAlchemyError
Expand Down Expand Up @@ -597,9 +598,6 @@ async def test_options_flow_db_url_empty(
"homeassistant.components.sql.async_setup_entry",
return_value=True,
),
patch(
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
),
):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
Expand All @@ -621,28 +619,29 @@ async def test_options_flow_db_url_empty(


async def test_full_flow_not_recorder_db(
recorder_mock: Recorder, hass: HomeAssistant
recorder_mock: Recorder,
hass: HomeAssistant,
tmp_path: Path,
) -> None:
"""Test full config flow with not using recorder db."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
db_path = tmp_path / "db.db"
db_path_str = f"sqlite:///{db_path}"

with (
patch(
"homeassistant.components.sql.async_setup_entry",
return_value=True,
),
patch(
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
),
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"db_url": "sqlite://path/to/db.db",
"db_url": db_path_str,
"name": "Get Value",
"query": "SELECT 5 as value",
"column": "value",
Expand All @@ -654,7 +653,7 @@ async def test_full_flow_not_recorder_db(
assert result2["title"] == "Get Value"
assert result2["options"] == {
"name": "Get Value",
"db_url": "sqlite://path/to/db.db",
"db_url": db_path_str,
"query": "SELECT 5 as value",
"column": "value",
}
Expand All @@ -671,15 +670,12 @@ async def test_full_flow_not_recorder_db(
"homeassistant.components.sql.async_setup_entry",
return_value=True,
),
patch(
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
),
):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
"query": "SELECT 5 as value",
"db_url": "sqlite://path/to/db.db",
"db_url": db_path_str,
"column": "value",
"unit_of_measurement": "MiB",
},
Expand All @@ -689,40 +685,38 @@ async def test_full_flow_not_recorder_db(
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {
"name": "Get Value",
"db_url": "sqlite://path/to/db.db",
"db_url": db_path_str,
"query": "SELECT 5 as value",
"column": "value",
"unit_of_measurement": "MiB",
}

# Need to test same again to mitigate issue with db_url removal
result = await hass.config_entries.options.async_init(entry.entry_id)
with patch(
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
"query": "SELECT 5 as value",
"db_url": "sqlite://path/to/db.db",
"column": "value",
"unit_of_measurement": "MB",
},
)
await hass.async_block_till_done()

result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
"query": "SELECT 5 as value",
"db_url": db_path_str,
"column": "value",
"unit_of_measurement": "MB",
},
)
await hass.async_block_till_done()

assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {
"name": "Get Value",
"db_url": "sqlite://path/to/db.db",
"db_url": db_path_str,
"query": "SELECT 5 as value",
"column": "value",
"unit_of_measurement": "MB",
}

assert entry.options == {
"name": "Get Value",
"db_url": "sqlite://path/to/db.db",
"db_url": db_path_str,
"query": "SELECT 5 as value",
"column": "value",
"unit_of_measurement": "MB",
Expand Down
41 changes: 25 additions & 16 deletions tests/components/sql/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
from __future__ import annotations

from datetime import timedelta
from pathlib import Path
import sqlite3
from typing import Any
from unittest.mock import patch

from freezegun.api import FrozenDateTimeFactory
import pytest
from sqlalchemy import text as sql_text
from sqlalchemy.exc import SQLAlchemyError

from homeassistant.components.recorder import Recorder
Expand Down Expand Up @@ -143,29 +144,37 @@ async def test_query_no_value(
assert text in caplog.text


async def test_query_mssql_no_result(
recorder_mock: Recorder, hass: HomeAssistant, caplog: pytest.LogCaptureFixture
async def test_query_on_disk_sqlite_no_result(
recorder_mock: Recorder,
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
tmp_path: Path,
) -> None:
"""Test the SQL sensor with a query that returns no value."""
db_path = tmp_path / "test.db"
db_path_str = f"sqlite:///{db_path}"

def make_test_db():
"""Create a test database."""
conn = sqlite3.connect(db_path)
conn.execute("CREATE TABLE users (value INTEGER)")
conn.commit()
conn.close()

await hass.async_add_executor_job(make_test_db)

config = {
"db_url": "mssql://",
"query": "SELECT 5 as value where 1=2",
"db_url": db_path_str,
"query": "SELECT value from users",
"column": "value",
"name": "count_tables",
"name": "count_users",
}
with (
patch("homeassistant.components.sql.sensor.sqlalchemy"),
patch(
"homeassistant.components.sql.sensor.sqlalchemy.text",
return_value=sql_text("SELECT TOP 1 5 as value where 1=2"),
),
):
await init_integration(hass, config)
await init_integration(hass, config)

state = hass.states.get("sensor.count_tables")
state = hass.states.get("sensor.count_users")
assert state.state == STATE_UNKNOWN

text = "SELECT TOP 1 5 AS VALUE WHERE 1=2 returned no results"
text = "SELECT value from users LIMIT 1; returned no results"
assert text in caplog.text


Expand Down

0 comments on commit 4326689

Please sign in to comment.