mssql-python 1.16.0__cp315-cp315-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation.
3
+ Licensed under the MIT license.
4
+ This module provides a way to create a new connection object to interact with the database.
5
+ """
6
+
7
+ from typing import Any, Dict, Optional, Union
8
+
9
+ from mssql_python.connection import Connection, TokenProvider
10
+
11
+
12
+ def connect(
13
+ connection_str: str = "",
14
+ autocommit: bool = False,
15
+ attrs_before: Optional[Dict[int, Union[int, str, bytes]]] = None,
16
+ timeout: int = 0,
17
+ native_uuid: Optional[bool] = None,
18
+ token_provider: Optional[TokenProvider] = None,
19
+ **kwargs: Any,
20
+ ) -> Connection:
21
+ """
22
+ Constructor for creating a connection to the database.
23
+
24
+ Args:
25
+ connection_str (str): The connection string to connect to.
26
+ autocommit (bool): If True, causes a commit to be performed after each SQL statement.
27
+ attrs_before (dict, optional): A dictionary of connection attributes to set before
28
+ connecting.
29
+ timeout (int): Login (connection-attempt) timeout in seconds. 0 (default)
30
+ uses the driver default. This bounds the login/network handshake (via
31
+ SQL_ATTR_LOGIN_TIMEOUT), not query execution — it is distinct from the
32
+ ``Connection.timeout`` property, which is the per-statement query
33
+ timeout. If both ``timeout`` and ``attrs_before[SQL_ATTR_LOGIN_TIMEOUT]``
34
+ are supplied, the explicit ``attrs_before`` value takes precedence.
35
+ native_uuid (bool, optional): Controls whether UNIQUEIDENTIFIER columns return
36
+ uuid.UUID objects (True) or str (False) for this connection.
37
+ - True: UNIQUEIDENTIFIER columns return uuid.UUID objects.
38
+ - False: UNIQUEIDENTIFIER columns return str (pyodbc-compatible).
39
+ - None (default): Uses the module-level ``mssql_python.native_uuid`` setting (True).
40
+
41
+ This per-connection override is useful for migration from pyodbc:
42
+ connections that need string UUIDs can pass native_uuid=False, while the default (True)
43
+ returns native uuid.UUID objects.
44
+ token_provider (object, optional): A token provider for Microsoft Entra ID
45
+ authentication. This must be any object with a ``.get_token(scope)`` method that
46
+ returns an object with a ``.token`` attribute containing a raw JWT string — for
47
+ example, any ``azure-identity`` credential class such as
48
+ ``DefaultAzureCredential``, ``AzureCliCredential``, ``ManagedIdentityCredential``,
49
+ ``CertificateCredential``, etc.
50
+
51
+ When provided, the driver calls ``token_provider.get_token()`` to acquire an
52
+ access token for SQL Server, bypassing the built-in credential map.
53
+ Cannot be combined with ``Authentication=`` in the connection string.
54
+
55
+ For environment-portable code, prefer ``Authentication=ActiveDirectoryDefault``
56
+ in the connection string — ``DefaultAzureCredential`` automatically picks the
57
+ right credential per environment (CLI on dev, Managed Identity in prod).
58
+ Use ``token_provider=`` only when you need explicit control over token
59
+ acquisition (e.g., excluding specific providers, using a credential not in
60
+ the built-in map, or passing custom options to the credential constructor).
61
+
62
+ Example::
63
+
64
+ from azure.identity import AzureCliCredential
65
+ conn = mssql_python.connect("Server=s;Database=d",
66
+ token_provider=AzureCliCredential())
67
+
68
+ Note: the token scope is fixed to the Azure **commercial** cloud
69
+ (``https://database.windows.net/.default``). Sovereign clouds (Azure US
70
+ Government, Azure China, Azure Germany) are **out of scope** — acquire the token
71
+ yourself and pass it via ``attrs_before[SQL_COPT_SS_ACCESS_TOKEN]`` instead.
72
+ Keyword Args:
73
+ **kwargs: Additional key/value pairs for the connection string.
74
+ Below attributes are not implemented in the internal driver:
75
+ - encoding (str): The encoding for the connection string.
76
+ - ansi (bool): If True, indicates the driver does not support Unicode.
77
+
78
+ Returns:
79
+ Connection: A new connection object to interact with the database.
80
+
81
+ Raises:
82
+ DatabaseError: If there is an error while trying to connect to the database.
83
+ InterfaceError: If there is an error related to the database interface.
84
+
85
+ This function provides a way to create a new connection object, which can then
86
+ be used to perform database operations such as executing queries, committing
87
+ transactions, and closing the connection.
88
+ """
89
+ conn = Connection(
90
+ connection_str,
91
+ autocommit=autocommit,
92
+ attrs_before=attrs_before,
93
+ timeout=timeout,
94
+ native_uuid=native_uuid,
95
+ token_provider=token_provider,
96
+ **kwargs,
97
+ )
98
+ return conn
@@ -0,0 +1,215 @@
1
+ """
2
+ Dynamic loading of platform-specific DDBC bindings for mssql-python.
3
+
4
+ This module handles the runtime loading of the appropriate compiled extension
5
+ module based on the current platform, architecture, and Python version.
6
+ """
7
+
8
+ import os
9
+ import importlib.util
10
+ import sys
11
+ import platform
12
+ import sysconfig
13
+ import warnings
14
+
15
+
16
+ def normalize_architecture(platform_name_param, architecture_param):
17
+ """
18
+ Normalize architecture names for the given platform.
19
+
20
+ Args:
21
+ platform_name_param (str): Platform name ('windows', 'darwin', 'linux')
22
+ architecture_param (str): Architecture string to normalize
23
+
24
+ Returns:
25
+ str: Normalized architecture name
26
+
27
+ Raises:
28
+ ImportError: If architecture is not supported for the given platform
29
+ OSError: If platform is not supported
30
+ """
31
+ arch_lower = architecture_param.lower()
32
+
33
+ if platform_name_param == "windows":
34
+ arch_map = {
35
+ "win64": "x64",
36
+ "amd64": "x64",
37
+ "x64": "x64",
38
+ "win32": "x86",
39
+ "x86": "x86",
40
+ "arm64": "arm64",
41
+ }
42
+ if arch_lower in arch_map:
43
+ return arch_map[arch_lower]
44
+ supported = list(set(arch_map.keys()))
45
+ raise ImportError(
46
+ f"Unsupported architecture '{architecture_param}' for platform "
47
+ f"'{platform_name_param}'; expected one of {supported}"
48
+ )
49
+
50
+ if platform_name_param == "darwin":
51
+ # For macOS, return runtime architecture
52
+ return platform.machine().lower()
53
+
54
+ if platform_name_param == "linux":
55
+ arch_map = {
56
+ "x64": "x86_64",
57
+ "amd64": "x86_64",
58
+ "x86_64": "x86_64",
59
+ "arm64": "arm64",
60
+ "aarch64": "arm64",
61
+ }
62
+ if arch_lower in arch_map:
63
+ return arch_map[arch_lower]
64
+ supported = list(set(arch_map.keys()))
65
+ raise ImportError(
66
+ f"Unsupported architecture '{architecture_param}' for platform "
67
+ f"'{platform_name_param}'; expected one of {supported}"
68
+ )
69
+
70
+ supported_platforms_list = ["windows", "darwin", "linux"]
71
+ raise OSError(
72
+ f"Unsupported platform '{platform_name_param}'; expected one of "
73
+ f"{supported_platforms_list}"
74
+ )
75
+
76
+
77
+ def get_interpreter_architecture(platform_name_param):
78
+ """
79
+ Get the raw architecture string of the running interpreter.
80
+
81
+ On Windows, ``platform.machine()`` reports the host CPU rather than the
82
+ architecture the interpreter was built for. The two differ when an x64
83
+ interpreter runs emulated on a Windows ARM64 machine (the python.org
84
+ "64-bit" installer is x64), so the loader would look for an arm64 binary
85
+ that the installed win_amd64 wheel never shipped. ``sysconfig.get_platform()``
86
+ is derived from the interpreter build and matches the wheel tag, so it is
87
+ the source of truth on Windows.
88
+
89
+ Args:
90
+ platform_name_param (str): Platform name ('windows', 'darwin', 'linux')
91
+
92
+ Returns:
93
+ str: Lower case architecture string accepted by normalize_architecture()
94
+ """
95
+ if platform_name_param == "windows":
96
+ # 'win-amd64' -> 'amd64', 'win-arm64' -> 'arm64', 'win32' -> 'win32'
97
+ return sysconfig.get_platform().lower().removeprefix("win-")
98
+ return platform.machine().lower()
99
+
100
+
101
+ def get_module_architecture(platform_name_param):
102
+ """
103
+ Get the architecture token used in the compiled ddbc_bindings filename.
104
+
105
+ Args:
106
+ platform_name_param (str): Platform name ('windows', 'darwin', 'linux')
107
+
108
+ Returns:
109
+ str: 'universal2' on macOS, otherwise the normalized architecture with
110
+ the Windows x64 build renamed to 'amd64' and the Windows x86 build
111
+ renamed to 'win32', matching the file names the build produces
112
+ """
113
+ # Special handling for macOS universal2 binaries
114
+ if platform_name_param == "darwin":
115
+ return "universal2"
116
+
117
+ architecture_name = normalize_architecture(
118
+ platform_name_param, get_interpreter_architecture(platform_name_param)
119
+ )
120
+
121
+ # Handle Windows-specific naming for binary files. The build names the
122
+ # x64 artifact 'amd64' and the x86 artifact 'win32' (see pybind/build.bat
123
+ # and pybind/CMakeLists.txt), so the loader has to look for those tokens.
124
+ if platform_name_param == "windows" and architecture_name == "x64":
125
+ architecture_name = "amd64"
126
+ elif platform_name_param == "windows" and architecture_name == "x86":
127
+ architecture_name = "win32"
128
+ return architecture_name
129
+
130
+
131
+ def find_module_path(module_dir_param, python_version_param, architecture_param, extension_param):
132
+ """
133
+ Find the compiled ddbc_bindings module file for the running interpreter.
134
+
135
+ Args:
136
+ module_dir_param (str): Directory that contains the compiled module
137
+ python_version_param (str): Python version tag such as 'cp312'
138
+ architecture_param (str): Architecture token from get_module_architecture()
139
+ extension_param (str): File extension, '.pyd' on Windows and '.so' elsewhere
140
+
141
+ Returns:
142
+ str: Path of the exactly matching module file. If it does not exist, the
143
+ first ddbc_bindings file built for this interpreter's version tag is
144
+ returned instead and a RuntimeWarning is emitted.
145
+
146
+ Raises:
147
+ ImportError: If no ddbc_bindings file for this interpreter's version
148
+ tag exists with the right extension
149
+ """
150
+ expected_module = f"ddbc_bindings.{python_version_param}-{architecture_param}{extension_param}"
151
+ module_path_found = os.path.join(module_dir_param, expected_module)
152
+
153
+ if os.path.exists(module_path_found):
154
+ return module_path_found
155
+
156
+ # Fallback: only binaries built for this interpreter's version tag are
157
+ # loadable, since the cpXY tags are version specific rather than abi3. A
158
+ # different architecture may still work (for example a universal2 build),
159
+ # but a different Python version fails later inside importlib with a
160
+ # confusing DLL or symbol error, so it is excluded here. Sorted so the
161
+ # pick is deterministic when several candidates remain.
162
+ module_files = sorted(
163
+ f
164
+ for f in os.listdir(module_dir_param)
165
+ if f.startswith(f"ddbc_bindings.{python_version_param}-") and f.endswith(extension_param)
166
+ )
167
+ if not module_files:
168
+ raise ImportError(
169
+ f"No ddbc_bindings module found for {python_version_param}-{architecture_param} "
170
+ f"with extension {extension_param}"
171
+ )
172
+ warnings.warn(
173
+ f"Using fallback module file {module_files[0]} instead of {expected_module}",
174
+ RuntimeWarning,
175
+ stacklevel=2,
176
+ )
177
+ return os.path.join(module_dir_param, module_files[0])
178
+
179
+
180
+ # Get current Python version and architecture
181
+ python_version = f"cp{sys.version_info.major}{sys.version_info.minor}"
182
+
183
+ platform_name = platform.system().lower()
184
+ architecture = get_module_architecture(platform_name)
185
+
186
+ # Validate supported platforms
187
+ if platform_name not in ["windows", "darwin", "linux"]:
188
+ supported_platforms = ["windows", "darwin", "linux"]
189
+ raise ImportError(
190
+ f"Unsupported platform '{platform_name}' for mssql-python; expected one "
191
+ f"of {supported_platforms}"
192
+ )
193
+
194
+ # Determine extension based on platform
195
+ if platform_name == "windows":
196
+ extension = ".pyd"
197
+ else: # macOS or Linux
198
+ extension = ".so"
199
+
200
+ # Find the specifically matching module file
201
+ module_dir = os.path.dirname(__file__)
202
+ module_path = find_module_path(module_dir, python_version, architecture, extension)
203
+
204
+
205
+ # Use the original module name 'ddbc_bindings' that the C extension was compiled with
206
+ module_name = "ddbc_bindings"
207
+ spec = importlib.util.spec_from_file_location(module_name, module_path)
208
+ module = importlib.util.module_from_spec(spec)
209
+ sys.modules[module_name] = module
210
+ spec.loader.exec_module(module)
211
+
212
+ # Copy all attributes from the loaded module to this module
213
+ for attr in dir(module):
214
+ if not attr.startswith("__"):
215
+ globals()[attr] = getattr(module, attr)
@@ -0,0 +1,114 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation.
3
+ Licensed under the MIT license.
4
+
5
+ This module provides functions for managing decimal separator configuration.
6
+ """
7
+
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING: # pragma: no cover
11
+ from mssql_python.helpers import Settings
12
+
13
+
14
+ def _setDecimalSeparator(separator: str, settings: "Settings", set_in_cpp_func=None) -> None:
15
+ """
16
+ Internal implementation for setting the decimal separator.
17
+
18
+ Not intended for direct external use — the public API is exposed via
19
+ ``mssql_python.setDecimalSeparator`` (created by ``create_decimal_separator_functions``).
20
+
21
+ Args:
22
+ separator (str): The character to use as decimal separator
23
+ settings (Settings): The settings object to update
24
+ set_in_cpp_func: Optional callable to set the separator in C++ bindings
25
+
26
+ Raises:
27
+ ValueError: If the separator is not a single character string
28
+ """
29
+ # Type validation
30
+ if not isinstance(separator, str):
31
+ raise ValueError("Decimal separator must be a string")
32
+
33
+ # Length validation
34
+ if len(separator) == 0:
35
+ raise ValueError("Decimal separator cannot be empty")
36
+
37
+ if len(separator) > 1:
38
+ raise ValueError("Decimal separator must be a single character")
39
+
40
+ # Character validation (covers \t, \n, \r, \v, \f and all other whitespace)
41
+ if separator.isspace():
42
+ raise ValueError("Whitespace characters are not allowed as decimal separators")
43
+
44
+ # Set in Python side settings
45
+ settings.decimal_separator = separator
46
+
47
+ # Update the C++ side if available
48
+ if set_in_cpp_func is not None:
49
+ set_in_cpp_func(separator)
50
+
51
+
52
+ def _getDecimalSeparator(settings: "Settings") -> str:
53
+ """
54
+ Internal implementation for getting the decimal separator.
55
+
56
+ Not intended for direct external use — the public API is exposed via
57
+ ``mssql_python.getDecimalSeparator`` (created by ``create_decimal_separator_functions``).
58
+
59
+ Args:
60
+ settings (Settings): The settings object to read from
61
+
62
+ Returns:
63
+ str: The current decimal separator character
64
+ """
65
+ return settings.decimal_separator
66
+
67
+
68
+ def create_decimal_separator_functions(settings: "Settings"):
69
+ """
70
+ Factory function to create decimal separator getter/setter bound to specific settings.
71
+
72
+ This function handles importing the C++ binding and initializing the decimal separator.
73
+
74
+ Args:
75
+ settings: The Settings instance to bind to
76
+
77
+ Returns:
78
+ Tuple of (setDecimalSeparator, getDecimalSeparator) functions
79
+ """
80
+ # Try to import and initialize the C++ binding
81
+ try:
82
+ from mssql_python.ddbc_bindings import DDBCSetDecimalSeparator
83
+
84
+ # Set the initial decimal separator in C++
85
+ DDBCSetDecimalSeparator(settings.decimal_separator)
86
+ cpp_binding = DDBCSetDecimalSeparator
87
+ except ImportError:
88
+ # Handle case where ddbc_bindings is not available
89
+ cpp_binding = None
90
+
91
+ def setter(separator: str) -> None:
92
+ """
93
+ Sets the decimal separator character used when parsing NUMERIC/DECIMAL values
94
+ from the database, e.g. the "." in "1,234.56".
95
+
96
+ Args:
97
+ separator (str): The character to use as decimal separator
98
+
99
+ Raises:
100
+ ValueError: If the separator is not a single character string
101
+ """
102
+ _setDecimalSeparator(separator, settings, cpp_binding)
103
+
104
+ def getter() -> str:
105
+ """
106
+ Returns the decimal separator character used when parsing NUMERIC/DECIMAL values
107
+ from the database.
108
+
109
+ Returns:
110
+ str: The current decimal separator character
111
+ """
112
+ return _getDecimalSeparator(settings)
113
+
114
+ return setter, getter