CHORE: Add standalone mssql-python-odbc package for ODBC driver binaries#663
Open
jahnvi480 wants to merge 10 commits into
Open
CHORE: Add standalone mssql-python-odbc package for ODBC driver binaries#663jahnvi480 wants to merge 10 commits into
jahnvi480 wants to merge 10 commits into
Conversation
Introduce mssql_python_odbc, a pure-data sibling package (v18.6.0) that ships the ODBC driver libs/ tree, plus setup_odbc.py which builds platform-specific, Python-agnostic (py3-none-<plat>) wheels for the 7 supported platforms. ddbc_bindings.cpp now resolves the driver/libs base directory from mssql_python_odbc when installed, and falls back to the bundled mssql_python libs during the Phase-2 transition (bundled libs are retained). Add tests/test_024_odbc_package_split.py to verify Python/C++ driver-path parity, and .gitignore entries for local build copies.
Contributor
There was a problem hiding this comment.
Pull request overview
Splits the bundled ODBC driver binaries out of mssql-python into a new standalone data-only package (mssql-python-odbc / mssql_python_odbc), updates the native loader to prefer the external package with a fallback, and adds parity tests to ensure Python and C++ resolve identical driver paths.
Changes:
- Add new
mssql_python_odbcpackage withget_driver_path()/get_libs_dir()and platform/arch/distro detection mirroring the C++ resolver. - Add
setup_odbc.pyto build platform-specific, Python-agnostic wheels for the ODBC-binaries package, including a local “sync libs” convenience. - Update
ddbc_bindings.cppto resolve the ODBC libs base dir viamssql_python_odbcwhen installed, with fallback to bundledmssql_pythonlibs; add tests validating Python/C++ path parity.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
tests/test_024_odbc_package_split.py |
Adds test coverage validating the new package’s API and Python/C++ driver-path parity. |
setup_odbc.py |
Introduces a dedicated wheel build script for the new ODBC-binaries package and a transition-time libs sync. |
mssql_python/pybind/ddbc_bindings.cpp |
Switches native driver/libs base-dir resolution to prefer mssql_python_odbc, falling back to bundled libs. |
mssql_python_odbc/__init__.py |
Implements the new Python API for locating driver binaries within the standalone package. |
.gitignore |
Ignores transition-time generated/copy artifacts for the new package (libs/, egg-info). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3 tasks
📊 Code Coverage Report
Diff CoverageDiff: main...HEAD, staged and unstaged changes
Summary
mssql_python/pybind/ddbc_bindings.cppLines 1132-1157 1132 // its directory as the base that `GetDriverPathCpp` (and the Windows
1133 // `mssql-auth.dll` lookup) append `libs` to.
1134 //
1135 // During the Phase-2 transition we fall back to the bundled `mssql_python`
! 1136 // directory when the external package is not installed, so a wheel that still
! 1137 // bundles `libs/` keeps working. Importing `mssql_python_odbc` here is
! 1138 // Alpine/musl-safe precisely because it is a separate pure package: it cannot
1139 // trigger the partially-initialized-module circular import that motivated
1140 // resolving these paths in C++ in the first place.
1141 //
! 1142 // (`GetDriverPathCpp` is defined further below; forward-declared here so we can
1143 // verify the external package actually ships this platform's driver binary.)
1144 std::string GetDriverPathCpp(const std::string& moduleDir);
1145
! 1146 std::string GetOdbcLibsBaseDir() {
! 1147 namespace fs = std::filesystem;
! 1148 // This function calls into the Python C-API (py::module::import, attribute
1149 // access, casts), so it must run with the GIL held. It is a no-op when the
1150 // GIL is already held — which it is at the sole current call site, during
! 1151 // module initialization — but acquiring it here self-documents the C-API
! 1152 // dependency and keeps a future GIL-released caller from turning this into a
! 1153 // hard crash.
1154 py::gil_scoped_acquire gil;
1155 try {
1156 py::object module = py::module::import("mssql_python_odbc");
1157 py::object module_path = module.attr("__file__");Lines 1171-1180 1171 // unconditionally. Verifying both here keeps this resolver's notion of a
1172 // usable base dir consistent with what the loader below actually needs,
1173 // so we never select a directory that would later make the loader throw
1174 // (e.g. a dir that has msodbcsql18.dll but is missing mssql-auth.dll).
! 1175 std::error_code ec;
! 1176 fs::path externalDriver(GetDriverPathCpp(parentDir.string()));
1177 bool externalComplete = fs::exists(externalDriver, ec);
1178 #ifdef _WIN32
1179 if (externalComplete) {
1180 fs::path externalAuthDll = externalDriver.parent_path() / "mssql-auth.dll";Lines 1178-1186 1178 #ifdef _WIN32
1179 if (externalComplete) {
1180 fs::path externalAuthDll = externalDriver.parent_path() / "mssql-auth.dll";
1181 externalComplete = fs::exists(externalAuthDll, ec);
! 1182 }
1183 #endif
1184 if (externalComplete) {
1185 LOG("GetOdbcLibsBaseDir: Using external mssql_python_odbc package - directory='%s'",
1186 parentDir.string().c_str());Lines 1191-1202 1191 parentDir.string().c_str());
1192 return GetModuleDirectory();
1193 } catch (const py::error_already_set& e) {
1194 if (e.matches(PyExc_ModuleNotFoundError)) {
! 1195 // Expected in Phase 2 when the standalone package is not installed.
! 1196 // pybind11 has already fetched and cleared the CPython error
! 1197 // indicator, so re-importing `mssql_python` below is safe.
! 1198 LOG("GetOdbcLibsBaseDir: mssql_python_odbc not installed (%s); "
1199 "falling back to bundled libs in mssql_python",
1200 e.what());
1201 return GetModuleDirectory();
1202 }📋 Files Needing Attention📉 Files with overall lowest coverage (click to expand)mssql_python.pybind.logger_bridge.cpp: 59.2%
mssql_python.pybind.ddbc_bindings.h: 59.9%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.ddbc_bindings.cpp: 76.1%
mssql_python.pybind.connection.connection.cpp: 76.2%
mssql_python.__init__.py: 77.3%
mssql_python.row.py: 77.6%
mssql_python.ddbc_bindings.py: 79.6%
mssql_python.connection.py: 83.6%
mssql_python.logging.py: 85.5%🔗 Quick Links
|
…rsion coupling, guard minimum setuptools
Corrects the earlier 'parity' framing. Main does not strip the VC++ runtime: build.bat relocates msvcp140.dll next to the .pyd and setup.py excludes the duplicate vcredist/ folder (de-duplication). The odbc package has no .pyd, so it keeps msvcp140.dll in vcredist/ with its license. No Phase-2 skew: the driver's runtime dependency is satisfied in-process by the /MD-built extension in both paths.
jahnvi480
added a commit
that referenced
this pull request
Jul 8, 2026
Per review feedback, build and release the standalone mssql-python-odbc package from the existing Build-Release-Package-Pipeline (def 2199) and official release pipeline instead of separate ADO definitions. Build pipeline: add odbcWindows/Macos/LinuxConfigs params (2+1+4=7 data-only wheels), reference the 3 odbc stage templates alongside the mssql-python stages, and add a separate ConsolidateOdbc stage publishing drop_ConsolidateOdbc_ConsolidateArtifacts (distinct from mssql-python's drop_Consolidate_ConsolidateArtifacts). Release pipeline: add releasePackage parameter (mssql-python | mssql-python-odbc); select the consolidated artifact accordingly and skip symbol publishing + mssql-py-core version validation for odbc. Delete the redundant standalone orchestrators (build-release-odbc-pipeline.yml, official-release-odbc-pipeline.yml). This removes the need to register a new ADO build definition and the odbcBuildDefinitionId placeholder. Both packages now build from def 2199; the odbc stages consume setup_odbc.py from PR #663, so #663 must merge before these stages can build.
The comment above MIN_SETUPTOOLS claimed the libs/ tree is gitignored and that include_package_data cannot recover the binaries from version control. Both are inaccurate: the driver binaries are committed, and package_data globs enumerate files from the on-disk mssql_python_odbc/libs/ subtree, not from git. Replace with an accurate description; the real rationale for the guard (setuptools 62.3.0 '**' recursive-glob support) is retained.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work Item / Issue Reference
Summary
Splits the ODBC driver binaries out of
mssql-pythoninto a new standalone, pure-data packagemssql-python-odbc(import namemssql_python_odbc, version 18.6.0). This is the code / packaging half of the split; the ADO release pipelines are in companion PR #664.What is included
mssql_python_odbc/__init__.py- pure-Python package (no native extension) that ships the ODBC driverlibs/tree and exposesget_driver_path()andget_libs_dir()with the same per-platform layout the C++ loader expects.setup_odbc.py- builds themssql-python-odbcwheels. Each wheel is platform-specific but Python-agnostic (py3-none-<platform>), so we ship 7 wheels total (Windows amd64/arm64, macOS universal2, manylinux_2_28 x86_64/aarch64, musllinux_1_2 x86_64/aarch64) instead of a per-Python matrix. On Windows the wheel intentionally ships the VC++ runtime (vcredist/msvcp140.dll+ license) alongside the driver binaries - see "VC++ runtime" below.mssql_python/pybind/ddbc_bindings.cpp-LoadDriverOrThrowException()now resolves the driver / libs base directory via a newGetOdbcLibsBaseDir()helper, which importsmssql_python_odbcwhen present and falls back to the bundledmssql_pythonlibs when it is not. The fallback is GIL-safe and Alpine/musl-safe.tests/test_024_odbc_package_split.py- verifies Python vs C++ driver-path parity..gitignore- ignores localmssql_python_odbc/libs/build copies and egg-info.VC++ runtime (
vcredist) on WindowsThe Windows wheel ships
libs/windows/<arch>/vcredist/msvcp140.dll(with its license). This is a deliberate, documented choice - not "parity" with the main wheel's file layout, and not a reversal of a licensing/size decision:mssql-pythonwheel also ships the VC++ runtime, just relocated:build.batcopiesmsvcp140.dllout ofvcredist/to the package root next to the compiledddbc_bindingsextension, andsetup.pythen excludes the now-duplicatevcredist/folder. That exclusion is de-duplication, not a removal of the runtime..pydto place the runtime beside. Keepingmsvcp140.dllin its originalvcredist/folder keeps the driver binaries and the runtime they were built against together, and keeps the wheel self-contained.msodbcsql18.dll's dependency onmsvcp140.dllis satisfied in-process by the mssql-python extension (built/MD, which loadsmsvcp140.dllfrom beside the.pyd), so the external-package and bundled-libs paths behave identically. This copy is completeness + attribution, which is whyGetOdbcLibsBaseDir()'s completeness check verifies the driver +mssql-auth.dllbut notvcredist.Phasing (no breaking change in this PR)
mssql-pythonprefers the external package but keeps bundlinglibs/as a fallback, so existing installs keep working.libs/and depend onmssql-python-odbcexclusively.Testing
test_024parity suite: 7 passed, 1 skipped.mssql_python_odbc-18.6.0-py3-none-win_amd64.whllocally: correct wheel tag,Root-Is-Purelib: false, driver DLLs present,vcredist(msvcp140.dll+ license) present,twine checkpasses.Notes
Companion PR (release pipelines): #664. Infra registration (ESRP onboarding of
mssql-python-odbc, ADO build-definition id) is tracked there.