Skip to content

Commit 85f7984

Browse files
committed
fix(mssql-python): Constrain supported versions.
Signed-off-by: walanguzzi <walanguzzi@outlook.com>
1 parent 6c82402 commit 85f7984

3 files changed

Lines changed: 70 additions & 48 deletions

File tree

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ classifiers = [
4242
athena = ["PyAthena[Pandas]"]
4343
azuresql = ["pymssql"]
4444
azuresql-odbc = ["pyodbc>=5.0.0"]
45-
azuresql-mssql-python = ["mssql-python>=1.1.0"]
45+
azuresql-mssql-python = ["mssql-python>=1.1.0;python_version>=\"3.10\""]
4646
bigquery = [
4747
"google-cloud-bigquery[pandas]",
4848
"google-cloud-bigquery-storage"
@@ -85,7 +85,7 @@ dev = [
8585
"PyAthena[Pandas]",
8686
"PyGithub>=2.6.0",
8787
"pyodbc>=5.0.0",
88-
"mssql-python>=1.1.0",
88+
"mssql-python>=1.1.0;python_version>=\"3.10\"",
8989
"pyperf",
9090
"pyspark~=3.5.0",
9191
"pytest",
@@ -111,13 +111,13 @@ dbt = ["dbt-core<2"]
111111
dlt = ["dlt"]
112112
duckdb = []
113113
fabric = ["pyodbc>=5.0.0"]
114-
fabric-mssql-python = ["mssql-python>=1.1.0"]
114+
fabric-mssql-python = ["mssql-python>=1.1.0;python_version>=\"3.10\""]
115115
gcppostgres = ["cloud-sql-python-connector[pg8000]>=1.8.0"]
116116
github = ["PyGithub>=2.6.0"]
117117
motherduck = ["duckdb>=1.3.2"]
118118
mssql = ["pymssql"]
119119
mssql-odbc = ["pyodbc>=5.0.0"]
120-
mssql-python = ["mssql-python>=1.1.0"]
120+
mssql-python = ["mssql-python>=1.1.0;python_version>=\"3.10\""]
121121
mysql = ["pymysql"]
122122
mwaa = ["boto3"]
123123
postgres = ["psycopg2"]

sqlmesh/core/config/connection.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import typing as t
1111
from enum import Enum
1212
from functools import partial
13+
from sys import version_info
1314

1415
import pydantic
1516
from packaging import version
@@ -62,6 +63,7 @@
6263
}
6364
MOTHERDUCK_TOKEN_REGEX = re.compile(r"(\?|\&)(motherduck_token=)(\S*)")
6465
PASSWORD_REGEX = re.compile(r"(password=)(\S+)")
66+
SUPPORTS_MSSQL_PYTHON_DRIVER = (version_info.major, version_info.minor) >= (3, 10)
6567

6668

6769
def _get_engine_import_validator(
@@ -1631,6 +1633,9 @@ def _connection_factory(self) -> t.Callable:
16311633
# The `mssql-python` implementation is API-compatible with
16321634
# with the `pyodbc` equivalent for documented parameters.
16331635

1636+
if not SUPPORTS_MSSQL_PYTHON_DRIVER:
1637+
raise ConfigError("The `mssql-python` driver requires Python 3.10 or higher.")
1638+
16341639
def connect_mssql_python(**kwargs: t.Any) -> t.Callable:
16351640
# Extract parameters for connection string
16361641
host = kwargs.pop("host")

tests/core/test_connection_config.py

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from sqlmesh.core.config.connection import (
1111
INIT_DISPLAY_INFO_TO_TYPE,
12+
SUPPORTS_MSSQL_PYTHON_DRIVER,
1213
AthenaConnectionConfig,
1314
BigQueryConnectionConfig,
1415
ClickhouseConnectionConfig,
@@ -1516,10 +1517,11 @@ def test_mssql_engine_import_validator():
15161517
MSSQLConnectionConfig(host="localhost", driver="pyodbc")
15171518

15181519
# Test PyODBC driver suggests mssql-python extra when import fails
1519-
with pytest.raises(ConfigError, match=r"pip install \"sqlmesh\[mssql-python\]\""):
1520-
with patch("importlib.import_module") as mock_import:
1521-
mock_import.side_effect = ImportError("No module named 'mssql_python'")
1522-
MSSQLConnectionConfig(host="localhost", driver="mssql-python")
1520+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1521+
with pytest.raises(ConfigError, match=r"pip install \"sqlmesh\[mssql-python\]\""):
1522+
with patch("importlib.import_module") as mock_import:
1523+
mock_import.side_effect = ImportError("No module named 'mssql_python'")
1524+
MSSQLConnectionConfig(host="localhost", driver="mssql-python")
15231525

15241526
# Test PyMSSQL driver suggests mssql extra when import fails
15251527
with pytest.raises(ConfigError, match=r"pip install \"sqlmesh\[mssql\]\""):
@@ -1553,9 +1555,12 @@ def test_mssql_connection_config_parameter_validation(make_config):
15531555
assert config.driver == "pyodbc"
15541556

15551557
# Test explicit mssql-python driver
1556-
config = make_config(type="mssql", host="localhost", driver="mssql-python", check_import=False)
1557-
assert isinstance(config, MSSQLConnectionConfig)
1558-
assert config.driver == "mssql-python"
1558+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1559+
config = make_config(
1560+
type="mssql", host="localhost", driver="mssql-python", check_import=False
1561+
)
1562+
assert isinstance(config, MSSQLConnectionConfig)
1563+
assert config.driver == "mssql-python"
15591564

15601565
# Test explicit pymssql driver
15611566
config = make_config(type="mssql", host="localhost", driver="pymssql", check_import=False)
@@ -1580,19 +1585,20 @@ def test_mssql_connection_config_parameter_validation(make_config):
15801585
assert config.odbc_properties == {"Authentication": "ActiveDirectoryServicePrincipal"}
15811586

15821587
# Test mssql-python specific parameters
1583-
config = make_config(
1584-
type="mssql",
1585-
host="localhost",
1586-
driver="mssql-python",
1587-
trust_server_certificate=True,
1588-
encrypt=False,
1589-
odbc_properties={"Authentication": "ActiveDirectoryServicePrincipal"},
1590-
check_import=False,
1591-
)
1592-
assert isinstance(config, MSSQLConnectionConfig)
1593-
assert config.trust_server_certificate is True
1594-
assert config.encrypt is False
1595-
assert config.odbc_properties == {"Authentication": "ActiveDirectoryServicePrincipal"}
1588+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1589+
config = make_config(
1590+
type="mssql",
1591+
host="localhost",
1592+
driver="mssql-python",
1593+
trust_server_certificate=True,
1594+
encrypt=False,
1595+
odbc_properties={"Authentication": "ActiveDirectoryServicePrincipal"},
1596+
check_import=False,
1597+
)
1598+
assert isinstance(config, MSSQLConnectionConfig)
1599+
assert config.trust_server_certificate is True
1600+
assert config.encrypt is False
1601+
assert config.odbc_properties == {"Authentication": "ActiveDirectoryServicePrincipal"}
15961602

15971603
# Test pymssql specific parameters
15981604
config = make_config(
@@ -1655,30 +1661,32 @@ def test_mssql_connection_kwargs_keys():
16551661
assert "conn_properties" not in pyodbc_keys
16561662

16571663
# Test mssql-python driver keys
1658-
config = MSSQLConnectionConfig(host="localhost", driver="mssql-python", check_import=False)
1659-
mssql_python_keys = config._connection_kwargs_keys
1660-
expected_mssql_python_keys = {
1661-
"password",
1662-
"user",
1663-
"database",
1664-
"host",
1665-
"timeout",
1666-
"login_timeout",
1667-
"charset",
1668-
"appname",
1669-
"port",
1670-
"autocommit",
1671-
"trust_server_certificate",
1672-
"encrypt",
1673-
"odbc_properties",
1674-
}
1675-
assert mssql_python_keys == expected_mssql_python_keys
1676-
1677-
# Verify mssql-python keys don't include pymssql-specific parameters
1678-
assert "tds_version" not in mssql_python_keys
1679-
assert "conn_properties" not in mssql_python_keys
1680-
1681-
1664+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1665+
config = MSSQLConnectionConfig(host="localhost", driver="mssql-python", check_import=False)
1666+
mssql_python_keys = config._connection_kwargs_keys
1667+
expected_mssql_python_keys = {
1668+
"password",
1669+
"user",
1670+
"database",
1671+
"host",
1672+
"timeout",
1673+
"login_timeout",
1674+
"charset",
1675+
"appname",
1676+
"port",
1677+
"autocommit",
1678+
"trust_server_certificate",
1679+
"encrypt",
1680+
"odbc_properties",
1681+
}
1682+
assert mssql_python_keys == expected_mssql_python_keys
1683+
1684+
# Verify mssql-python keys don't include pymssql-specific parameters
1685+
assert "tds_version" not in mssql_python_keys
1686+
assert "conn_properties" not in mssql_python_keys
1687+
1688+
1689+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
16821690
def test_mssql_pyodbc_connection_string_generation():
16831691
"""Test pyodbc.connect gets invoked with the correct ODBC connection string."""
16841692
with patch("pyodbc.connect") as mock_pyodbc_connect:
@@ -1728,6 +1736,7 @@ def test_mssql_pyodbc_connection_string_generation():
17281736
assert call_args[1]["autocommit"] is False
17291737

17301738

1739+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
17311740
def test_mssql_pyodbc_connection_string_with_odbc_properties():
17321741
"""Test pyodbc connection string includes custom ODBC properties."""
17331742
with patch("pyodbc.connect") as mock_pyodbc_connect:
@@ -1766,6 +1775,7 @@ def test_mssql_pyodbc_connection_string_with_odbc_properties():
17661775
assert conn_str.count("TrustServerCertificate") == 1
17671776

17681777

1778+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
17691779
def test_mssql_pyodbc_connection_string_minimal():
17701780
"""Test pyodbc connection string with minimal configuration."""
17711781
with patch("pyodbc.connect") as mock_pyodbc_connect:
@@ -1792,6 +1802,7 @@ def test_mssql_pyodbc_connection_string_minimal():
17921802
assert mock_pyodbc_connect.call_args[1]["autocommit"] is True
17931803

17941804

1805+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
17951806
def test_mssql_mssql_python_connection_string_generation():
17961807
"""Test mssql_python.connect gets invoked with the correct ODBC connection string."""
17971808
with patch("mssql_python.connect") as mock_mssql_python_connect:
@@ -1840,6 +1851,7 @@ def test_mssql_mssql_python_connection_string_generation():
18401851
assert call_args[1]["autocommit"] is False
18411852

18421853

1854+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
18431855
def test_mssql_mssql_python_connection_string_with_odbc_properties():
18441856
"""Test mssql-python connection string includes custom ODBC properties."""
18451857
with patch("mssql_python.connect") as mock_mssql_python_connect:
@@ -1878,6 +1890,7 @@ def test_mssql_mssql_python_connection_string_with_odbc_properties():
18781890
assert conn_str.count("TrustServerCertificate") == 1
18791891

18801892

1893+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
18811894
def test_mssql_mssql_python_connection_string_minimal():
18821895
"""Test mssql-python connection string with minimal configuration."""
18831896
with patch("mssql_python.connect") as mock_mssql_python_connect:
@@ -2057,6 +2070,7 @@ def mock_add_output_converter(sql_type, converter_func):
20572070
assert result.tzinfo == timezone(timedelta(hours=-8))
20582071

20592072

2073+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
20602074
def test_mssql_mssql_python_connection_datetimeoffset_handling():
20612075
"""Test that the MSSQL mssql-python connection properly handles DATETIMEOFFSET conversion."""
20622076
import struct
@@ -2129,6 +2143,7 @@ def mock_add_output_converter(sql_type, converter_func):
21292143
assert result.tzinfo == timezone(timedelta(hours=5, minutes=30))
21302144

21312145

2146+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
21322147
def test_mssql_mssql_python_connection_negative_timezone_offset():
21332148
"""Test DATETIMEOFFSET handling with negative timezone offset at connection level."""
21342149
import struct
@@ -2228,6 +2243,7 @@ def test_fabric_pyodbc_connection_config_parameter_validation(make_config):
22282243
make_config(type="fabric", host="localhost", driver="pymssql", check_import=False)
22292244

22302245

2246+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
22312247
def test_fabric_mssql_python_connection_config_parameter_validation(make_config):
22322248
"""Test Fabric mssql-python connection config parameter validation."""
22332249
# Test that FabricConnectionConfig correctly handles mssql-python-specific parameters.
@@ -2300,6 +2316,7 @@ def test_fabric_pyodbc_connection_string_generation():
23002316
assert call_args[1]["autocommit"] is True
23012317

23022318

2319+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
23032320
def test_fabric_mssql_python_connection_string_generation():
23042321
"""Test that the Fabric mssql-python connection gets invoked with the correct connection string."""
23052322
with patch("mssql_python.connect") as mock_mssql_python_connect:

0 commit comments

Comments
 (0)