Skip to content

Commit f425f34

Browse files
fix(mssql-python): Constrain supported versions.
1 parent 0590fd5 commit f425f34

File tree

3 files changed

+70
-48
lines changed

3 files changed

+70
-48
lines changed

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.2.0"]
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
@@ -60,6 +61,7 @@
6061
}
6162
MOTHERDUCK_TOKEN_REGEX = re.compile(r"(\?|\&)(motherduck_token=)(\S*)")
6263
PASSWORD_REGEX = re.compile(r"(password=)(\S+)")
64+
SUPPORTS_MSSQL_PYTHON_DRIVER = (version_info.major, version_info.minor) >= (3, 10)
6365

6466

6567
def _get_engine_import_validator(
@@ -1622,6 +1624,9 @@ def _connection_factory(self) -> t.Callable:
16221624
# The `mssql-python` implementation is API-compatible with
16231625
# with the `pyodbc` equivalent for documented parameters.
16241626

1627+
if not SUPPORTS_MSSQL_PYTHON_DRIVER:
1628+
raise ConfigError("The `mssql-python` driver requires Python 3.10 or higher.")
1629+
16251630
def connect_mssql_python(**kwargs: t.Any) -> t.Callable:
16261631
# Extract parameters for connection string
16271632
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,
@@ -1464,10 +1465,11 @@ def test_mssql_engine_import_validator():
14641465
MSSQLConnectionConfig(host="localhost", driver="pyodbc")
14651466

14661467
# Test PyODBC driver suggests mssql-python extra when import fails
1467-
with pytest.raises(ConfigError, match=r"pip install \"sqlmesh\[mssql-python\]\""):
1468-
with patch("importlib.import_module") as mock_import:
1469-
mock_import.side_effect = ImportError("No module named 'mssql_python'")
1470-
MSSQLConnectionConfig(host="localhost", driver="mssql-python")
1468+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1469+
with pytest.raises(ConfigError, match=r"pip install \"sqlmesh\[mssql-python\]\""):
1470+
with patch("importlib.import_module") as mock_import:
1471+
mock_import.side_effect = ImportError("No module named 'mssql_python'")
1472+
MSSQLConnectionConfig(host="localhost", driver="mssql-python")
14711473

14721474
# Test PyMSSQL driver suggests mssql extra when import fails
14731475
with pytest.raises(ConfigError, match=r"pip install \"sqlmesh\[mssql\]\""):
@@ -1501,9 +1503,12 @@ def test_mssql_connection_config_parameter_validation(make_config):
15011503
assert config.driver == "pyodbc"
15021504

15031505
# Test explicit mssql-python driver
1504-
config = make_config(type="mssql", host="localhost", driver="mssql-python", check_import=False)
1505-
assert isinstance(config, MSSQLConnectionConfig)
1506-
assert config.driver == "mssql-python"
1506+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1507+
config = make_config(
1508+
type="mssql", host="localhost", driver="mssql-python", check_import=False
1509+
)
1510+
assert isinstance(config, MSSQLConnectionConfig)
1511+
assert config.driver == "mssql-python"
15071512

15081513
# Test explicit pymssql driver
15091514
config = make_config(type="mssql", host="localhost", driver="pymssql", check_import=False)
@@ -1528,19 +1533,20 @@ def test_mssql_connection_config_parameter_validation(make_config):
15281533
assert config.odbc_properties == {"Authentication": "ActiveDirectoryServicePrincipal"}
15291534

15301535
# Test mssql-python specific parameters
1531-
config = make_config(
1532-
type="mssql",
1533-
host="localhost",
1534-
driver="mssql-python",
1535-
trust_server_certificate=True,
1536-
encrypt=False,
1537-
odbc_properties={"Authentication": "ActiveDirectoryServicePrincipal"},
1538-
check_import=False,
1539-
)
1540-
assert isinstance(config, MSSQLConnectionConfig)
1541-
assert config.trust_server_certificate is True
1542-
assert config.encrypt is False
1543-
assert config.odbc_properties == {"Authentication": "ActiveDirectoryServicePrincipal"}
1536+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1537+
config = make_config(
1538+
type="mssql",
1539+
host="localhost",
1540+
driver="mssql-python",
1541+
trust_server_certificate=True,
1542+
encrypt=False,
1543+
odbc_properties={"Authentication": "ActiveDirectoryServicePrincipal"},
1544+
check_import=False,
1545+
)
1546+
assert isinstance(config, MSSQLConnectionConfig)
1547+
assert config.trust_server_certificate is True
1548+
assert config.encrypt is False
1549+
assert config.odbc_properties == {"Authentication": "ActiveDirectoryServicePrincipal"}
15441550

15451551
# Test pymssql specific parameters
15461552
config = make_config(
@@ -1603,30 +1609,32 @@ def test_mssql_connection_kwargs_keys():
16031609
assert "conn_properties" not in pyodbc_keys
16041610

16051611
# Test mssql-python driver keys
1606-
config = MSSQLConnectionConfig(host="localhost", driver="mssql-python", check_import=False)
1607-
mssql_python_keys = config._connection_kwargs_keys
1608-
expected_mssql_python_keys = {
1609-
"password",
1610-
"user",
1611-
"database",
1612-
"host",
1613-
"timeout",
1614-
"login_timeout",
1615-
"charset",
1616-
"appname",
1617-
"port",
1618-
"autocommit",
1619-
"trust_server_certificate",
1620-
"encrypt",
1621-
"odbc_properties",
1622-
}
1623-
assert mssql_python_keys == expected_mssql_python_keys
1624-
1625-
# Verify mssql-python keys don't include pymssql-specific parameters
1626-
assert "tds_version" not in mssql_python_keys
1627-
assert "conn_properties" not in mssql_python_keys
1628-
1629-
1612+
if SUPPORTS_MSSQL_PYTHON_DRIVER:
1613+
config = MSSQLConnectionConfig(host="localhost", driver="mssql-python", check_import=False)
1614+
mssql_python_keys = config._connection_kwargs_keys
1615+
expected_mssql_python_keys = {
1616+
"password",
1617+
"user",
1618+
"database",
1619+
"host",
1620+
"timeout",
1621+
"login_timeout",
1622+
"charset",
1623+
"appname",
1624+
"port",
1625+
"autocommit",
1626+
"trust_server_certificate",
1627+
"encrypt",
1628+
"odbc_properties",
1629+
}
1630+
assert mssql_python_keys == expected_mssql_python_keys
1631+
1632+
# Verify mssql-python keys don't include pymssql-specific parameters
1633+
assert "tds_version" not in mssql_python_keys
1634+
assert "conn_properties" not in mssql_python_keys
1635+
1636+
1637+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
16301638
def test_mssql_pyodbc_connection_string_generation():
16311639
"""Test pyodbc.connect gets invoked with the correct ODBC connection string."""
16321640
with patch("pyodbc.connect") as mock_pyodbc_connect:
@@ -1676,6 +1684,7 @@ def test_mssql_pyodbc_connection_string_generation():
16761684
assert call_args[1]["autocommit"] is False
16771685

16781686

1687+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
16791688
def test_mssql_pyodbc_connection_string_with_odbc_properties():
16801689
"""Test pyodbc connection string includes custom ODBC properties."""
16811690
with patch("pyodbc.connect") as mock_pyodbc_connect:
@@ -1714,6 +1723,7 @@ def test_mssql_pyodbc_connection_string_with_odbc_properties():
17141723
assert conn_str.count("TrustServerCertificate") == 1
17151724

17161725

1726+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
17171727
def test_mssql_pyodbc_connection_string_minimal():
17181728
"""Test pyodbc connection string with minimal configuration."""
17191729
with patch("pyodbc.connect") as mock_pyodbc_connect:
@@ -1740,6 +1750,7 @@ def test_mssql_pyodbc_connection_string_minimal():
17401750
assert mock_pyodbc_connect.call_args[1]["autocommit"] is True
17411751

17421752

1753+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
17431754
def test_mssql_mssql_python_connection_string_generation():
17441755
"""Test mssql_python.connect gets invoked with the correct ODBC connection string."""
17451756
with patch("mssql_python.connect") as mock_mssql_python_connect:
@@ -1788,6 +1799,7 @@ def test_mssql_mssql_python_connection_string_generation():
17881799
assert call_args[1]["autocommit"] is False
17891800

17901801

1802+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
17911803
def test_mssql_mssql_python_connection_string_with_odbc_properties():
17921804
"""Test mssql-python connection string includes custom ODBC properties."""
17931805
with patch("mssql_python.connect") as mock_mssql_python_connect:
@@ -1826,6 +1838,7 @@ def test_mssql_mssql_python_connection_string_with_odbc_properties():
18261838
assert conn_str.count("TrustServerCertificate") == 1
18271839

18281840

1841+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
18291842
def test_mssql_mssql_python_connection_string_minimal():
18301843
"""Test mssql-python connection string with minimal configuration."""
18311844
with patch("mssql_python.connect") as mock_mssql_python_connect:
@@ -2005,6 +2018,7 @@ def mock_add_output_converter(sql_type, converter_func):
20052018
assert result.tzinfo == timezone(timedelta(hours=-8))
20062019

20072020

2021+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
20082022
def test_mssql_mssql_python_connection_datetimeoffset_handling():
20092023
"""Test that the MSSQL mssql-python connection properly handles DATETIMEOFFSET conversion."""
20102024
import struct
@@ -2077,6 +2091,7 @@ def mock_add_output_converter(sql_type, converter_func):
20772091
assert result.tzinfo == timezone(timedelta(hours=5, minutes=30))
20782092

20792093

2094+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
20802095
def test_mssql_mssql_python_connection_negative_timezone_offset():
20812096
"""Test DATETIMEOFFSET handling with negative timezone offset at connection level."""
20822097
import struct
@@ -2176,6 +2191,7 @@ def test_fabric_pyodbc_connection_config_parameter_validation(make_config):
21762191
make_config(type="fabric", host="localhost", driver="pymssql", check_import=False)
21772192

21782193

2194+
@pytest.mark.xfail(not SUPPORTS_MSSQL_PYTHON_DRIVER, reason="mssql-python driver not supported")
21792195
def test_fabric_mssql_python_connection_config_parameter_validation(make_config):
21802196
"""Test Fabric mssql-python connection config parameter validation."""
21812197
# Test that FabricConnectionConfig correctly handles mssql-python-specific parameters.
@@ -2248,6 +2264,7 @@ def test_fabric_pyodbc_connection_string_generation():
22482264
assert call_args[1]["autocommit"] is True
22492265

22502266

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

0 commit comments

Comments
 (0)