Skip to content

Commit 1c59dd6

Browse files
authored
FEAT: Export Row class and refactor __init__.py (#270) (#474)
### Work Item / Issue Reference <!-- IMPORTANT: Please follow the PR template guidelines below. For mssql-python maintainers: Insert your ADO Work Item ID below For external contributors: Insert Github Issue number below Only one reference is required - either GitHub issue OR ADO Work Item. --> <!-- mssql-python maintainers: ADO Work Item --> > [AB#43062](https://sqlclientdrivers.visualstudio.com/c6d89619-62de-46a0-8b46-70b92a84d85e/_workitems/edit/43062) <!-- External contributors: GitHub Issue --> > GitHub Issue: #270 ------------------------------------------------------------------- ### Summary This pull request refactors how module-level constants and decimal separator configuration are handled in the `mssql_python` package. Constants are now dynamically exported from the `constants` module, and decimal separator functions are moved to a dedicated module for improved maintainability and clarity. The changes also add new exports for row objects and simplify imports. **Module-level constant export and API improvements:** * Constants such as SQL types and GetInfo constants are now dynamically exported from the `constants` module, allowing direct import from `mssql_python` and reducing manual duplication. The `get_info_constants` function is also moved and exported from `constants.py`. * All manual constant exports and the `get_info_constants` function implementation are removed from `__init__.py` in favor of wildcard imports from `constants.py`. **Decimal separator configuration refactor:** * The decimal separator getter/setter logic is moved to a new module `decimal_config.py`, which encapsulates validation and C++ binding logic. A factory function now creates bound getter/setter functions for use in `__init__.py`. * The previous implementation of decimal separator functions in `__init__.py` is removed and replaced with the new factory-based approach. **API and import enhancements:** * The `Row` object is now exported at the top level for easier access. * Import statements are clarified and simplified, including the settings import and constant exports. These changes improve the maintainability, clarity, and usability of the package's public API and configuration options.
1 parent 85f8659 commit 1c59dd6

4 files changed

Lines changed: 447 additions & 169 deletions

File tree

mssql_python/__init__.py

Lines changed: 176 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
import threading
1010
import types
1111
import weakref
12-
from typing import Dict
1312

14-
# Import settings from helpers to avoid circular imports
13+
# Import settings from helpers module
1514
from .helpers import Settings, get_settings, _settings, _settings_lock
1615

1716
# Driver version
@@ -61,11 +60,14 @@
6160
# Cursor Objects
6261
from .cursor import Cursor
6362

63+
# Row Objects
64+
from .row import Row
65+
6466
# Logging Configuration (Simplified single-level DEBUG system)
6567
from .logging import logger, setup_logging, driver_logger
6668

6769
# Constants
68-
from .constants import ConstantsDDBC, GetInfoConstants
70+
from .constants import ConstantsDDBC, GetInfoConstants, get_info_constants
6971

7072
# Pooling
7173
from .pooling import PoolingManager
@@ -119,97 +121,177 @@ def _cleanup_connections():
119121
paramstyle: str = "pyformat"
120122
threadsafety: int = 1
121123

122-
# Set the initial decimal separator in C++
123-
try:
124-
from .ddbc_bindings import DDBCSetDecimalSeparator
125-
126-
DDBCSetDecimalSeparator(_settings.decimal_separator)
127-
except ImportError:
128-
# Handle case where ddbc_bindings is not available
129-
DDBCSetDecimalSeparator = None
130-
131-
132-
# New functions for decimal separator control
133-
def setDecimalSeparator(separator: str) -> None:
134-
"""
135-
Sets the decimal separator character used when parsing NUMERIC/DECIMAL values
136-
from the database, e.g. the "." in "1,234.56".
137-
138-
The default is to use the current locale's "decimal_point" value when the module
139-
was first imported, or "." if the locale is not available. This function overrides
140-
the default.
141-
142-
Args:
143-
separator (str): The character to use as decimal separator
144-
145-
Raises:
146-
ValueError: If the separator is not a single character string
147-
"""
148-
# Type validation
149-
if not isinstance(separator, str):
150-
raise ValueError("Decimal separator must be a string")
151-
152-
# Length validation
153-
if len(separator) == 0:
154-
raise ValueError("Decimal separator cannot be empty")
155-
156-
if len(separator) > 1:
157-
raise ValueError("Decimal separator must be a single character")
158-
159-
# Character validation
160-
if separator.isspace():
161-
raise ValueError("Whitespace characters are not allowed as decimal separators")
162-
163-
# Check for specific disallowed characters
164-
if separator in ["\t", "\n", "\r", "\v", "\f"]:
165-
raise ValueError(
166-
f"Control character '{repr(separator)}' is not allowed as a decimal separator"
167-
)
168-
169-
# Set in Python side settings
170-
_settings.decimal_separator = separator
171-
172-
# Update the C++ side
173-
if DDBCSetDecimalSeparator is not None:
174-
DDBCSetDecimalSeparator(separator)
175-
176-
177-
def getDecimalSeparator() -> str:
178-
"""
179-
Returns the decimal separator character used when parsing NUMERIC/DECIMAL values
180-
from the database.
181-
182-
Returns:
183-
str: The current decimal separator character
184-
"""
185-
return _settings.decimal_separator
186-
187-
188-
# Export specific constants for setencoding()
189-
SQL_CHAR: int = ConstantsDDBC.SQL_CHAR.value
190-
SQL_WCHAR: int = ConstantsDDBC.SQL_WCHAR.value
191-
SQL_WMETADATA: int = -99
192-
193-
# Export connection attribute constants for set_attr()
194-
# Only include driver-level attributes that the SQL Server ODBC driver can handle directly
195-
196-
# Core driver-level attributes
197-
SQL_ATTR_ACCESS_MODE: int = ConstantsDDBC.SQL_ATTR_ACCESS_MODE.value
198-
SQL_ATTR_CONNECTION_TIMEOUT: int = ConstantsDDBC.SQL_ATTR_CONNECTION_TIMEOUT.value
199-
SQL_ATTR_CURRENT_CATALOG: int = ConstantsDDBC.SQL_ATTR_CURRENT_CATALOG.value
200-
SQL_ATTR_LOGIN_TIMEOUT: int = ConstantsDDBC.SQL_ATTR_LOGIN_TIMEOUT.value
201-
SQL_ATTR_PACKET_SIZE: int = ConstantsDDBC.SQL_ATTR_PACKET_SIZE.value
202-
SQL_ATTR_TXN_ISOLATION: int = ConstantsDDBC.SQL_ATTR_TXN_ISOLATION.value
203-
204-
# Transaction Isolation Level Constants
205-
SQL_TXN_READ_UNCOMMITTED: int = ConstantsDDBC.SQL_TXN_READ_UNCOMMITTED.value
206-
SQL_TXN_READ_COMMITTED: int = ConstantsDDBC.SQL_TXN_READ_COMMITTED.value
207-
SQL_TXN_REPEATABLE_READ: int = ConstantsDDBC.SQL_TXN_REPEATABLE_READ.value
208-
SQL_TXN_SERIALIZABLE: int = ConstantsDDBC.SQL_TXN_SERIALIZABLE.value
209-
210-
# Access Mode Constants
211-
SQL_MODE_READ_WRITE: int = ConstantsDDBC.SQL_MODE_READ_WRITE.value
212-
SQL_MODE_READ_ONLY: int = ConstantsDDBC.SQL_MODE_READ_ONLY.value
124+
# Create decimal separator control functions bound to our settings
125+
from .decimal_config import create_decimal_separator_functions
126+
127+
setDecimalSeparator, getDecimalSeparator = create_decimal_separator_functions(_settings)
128+
129+
# Import module-level constants from constants module
130+
from .constants import ( # noqa: F401
131+
# Enum classes
132+
AuthType,
133+
SQLTypes,
134+
# Helper function
135+
get_info_constants,
136+
# SQL Type constants (from ConstantsDDBC)
137+
SQL_CHAR,
138+
SQL_VARCHAR,
139+
SQL_LONGVARCHAR,
140+
SQL_WCHAR,
141+
SQL_WVARCHAR,
142+
SQL_WLONGVARCHAR,
143+
SQL_DECIMAL,
144+
SQL_NUMERIC,
145+
SQL_BIT,
146+
SQL_TINYINT,
147+
SQL_SMALLINT,
148+
SQL_INTEGER,
149+
SQL_BIGINT,
150+
SQL_REAL,
151+
SQL_FLOAT,
152+
SQL_DOUBLE,
153+
SQL_BINARY,
154+
SQL_VARBINARY,
155+
SQL_LONGVARBINARY,
156+
SQL_DATE,
157+
SQL_TIME,
158+
SQL_TIMESTAMP,
159+
SQL_TYPE_DATE,
160+
SQL_TYPE_TIME,
161+
SQL_TYPE_TIMESTAMP,
162+
SQL_GUID,
163+
SQL_XML,
164+
# Connection attribute constants
165+
SQL_ATTR_ACCESS_MODE,
166+
SQL_ATTR_CONNECTION_TIMEOUT,
167+
SQL_ATTR_CURRENT_CATALOG,
168+
SQL_ATTR_LOGIN_TIMEOUT,
169+
SQL_ATTR_PACKET_SIZE,
170+
SQL_ATTR_TXN_ISOLATION,
171+
# Transaction isolation levels
172+
SQL_TXN_READ_UNCOMMITTED,
173+
SQL_TXN_READ_COMMITTED,
174+
SQL_TXN_REPEATABLE_READ,
175+
SQL_TXN_SERIALIZABLE,
176+
# Access modes
177+
SQL_MODE_READ_WRITE,
178+
SQL_MODE_READ_ONLY,
179+
# Special constants
180+
SQL_WMETADATA,
181+
# GetInfoConstants (all exported as module-level constants)
182+
SQL_DRIVER_NAME,
183+
SQL_DRIVER_VER,
184+
SQL_DRIVER_ODBC_VER,
185+
SQL_DRIVER_HLIB,
186+
SQL_DRIVER_HENV,
187+
SQL_DRIVER_HDBC,
188+
SQL_DATA_SOURCE_NAME,
189+
SQL_DATABASE_NAME,
190+
SQL_SERVER_NAME,
191+
SQL_USER_NAME,
192+
SQL_SQL_CONFORMANCE,
193+
SQL_KEYWORDS,
194+
SQL_IDENTIFIER_CASE,
195+
SQL_IDENTIFIER_QUOTE_CHAR,
196+
SQL_SPECIAL_CHARACTERS,
197+
SQL_SQL92_ENTRY_SQL,
198+
SQL_SQL92_INTERMEDIATE_SQL,
199+
SQL_SQL92_FULL_SQL,
200+
SQL_SUBQUERIES,
201+
SQL_EXPRESSIONS_IN_ORDERBY,
202+
SQL_CORRELATION_NAME,
203+
SQL_SEARCH_PATTERN_ESCAPE,
204+
SQL_CATALOG_TERM,
205+
SQL_CATALOG_NAME_SEPARATOR,
206+
SQL_SCHEMA_TERM,
207+
SQL_TABLE_TERM,
208+
SQL_PROCEDURES,
209+
SQL_ACCESSIBLE_TABLES,
210+
SQL_ACCESSIBLE_PROCEDURES,
211+
SQL_CATALOG_NAME,
212+
SQL_CATALOG_USAGE,
213+
SQL_SCHEMA_USAGE,
214+
SQL_COLUMN_ALIAS,
215+
SQL_DESCRIBE_PARAMETER,
216+
SQL_TXN_CAPABLE,
217+
SQL_TXN_ISOLATION_OPTION,
218+
SQL_DEFAULT_TXN_ISOLATION,
219+
SQL_MULTIPLE_ACTIVE_TXN,
220+
SQL_TXN_ISOLATION_LEVEL,
221+
SQL_NUMERIC_FUNCTIONS,
222+
SQL_STRING_FUNCTIONS,
223+
SQL_DATETIME_FUNCTIONS,
224+
SQL_SYSTEM_FUNCTIONS,
225+
SQL_CONVERT_FUNCTIONS,
226+
SQL_LIKE_ESCAPE_CLAUSE,
227+
SQL_MAX_COLUMN_NAME_LEN,
228+
SQL_MAX_TABLE_NAME_LEN,
229+
SQL_MAX_SCHEMA_NAME_LEN,
230+
SQL_MAX_CATALOG_NAME_LEN,
231+
SQL_MAX_IDENTIFIER_LEN,
232+
SQL_MAX_STATEMENT_LEN,
233+
SQL_MAX_CHAR_LITERAL_LEN,
234+
SQL_MAX_BINARY_LITERAL_LEN,
235+
SQL_MAX_COLUMNS_IN_TABLE,
236+
SQL_MAX_COLUMNS_IN_SELECT,
237+
SQL_MAX_COLUMNS_IN_GROUP_BY,
238+
SQL_MAX_COLUMNS_IN_ORDER_BY,
239+
SQL_MAX_COLUMNS_IN_INDEX,
240+
SQL_MAX_TABLES_IN_SELECT,
241+
SQL_MAX_CONCURRENT_ACTIVITIES,
242+
SQL_MAX_DRIVER_CONNECTIONS,
243+
SQL_MAX_ROW_SIZE,
244+
SQL_MAX_USER_NAME_LEN,
245+
SQL_ACTIVE_CONNECTIONS,
246+
SQL_ACTIVE_STATEMENTS,
247+
SQL_DATA_SOURCE_READ_ONLY,
248+
SQL_NEED_LONG_DATA_LEN,
249+
SQL_GETDATA_EXTENSIONS,
250+
SQL_CURSOR_COMMIT_BEHAVIOR,
251+
SQL_CURSOR_ROLLBACK_BEHAVIOR,
252+
SQL_CURSOR_SENSITIVITY,
253+
SQL_BOOKMARK_PERSISTENCE,
254+
SQL_DYNAMIC_CURSOR_ATTRIBUTES1,
255+
SQL_DYNAMIC_CURSOR_ATTRIBUTES2,
256+
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1,
257+
SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2,
258+
SQL_STATIC_CURSOR_ATTRIBUTES1,
259+
SQL_STATIC_CURSOR_ATTRIBUTES2,
260+
SQL_KEYSET_CURSOR_ATTRIBUTES1,
261+
SQL_KEYSET_CURSOR_ATTRIBUTES2,
262+
SQL_SCROLL_OPTIONS,
263+
SQL_SCROLL_CONCURRENCY,
264+
SQL_FETCH_DIRECTION,
265+
SQL_ROWSET_SIZE,
266+
SQL_CONCURRENCY,
267+
SQL_ROW_NUMBER,
268+
SQL_STATIC_SENSITIVITY,
269+
SQL_BATCH_SUPPORT,
270+
SQL_BATCH_ROW_COUNT,
271+
SQL_PARAM_ARRAY_ROW_COUNTS,
272+
SQL_PARAM_ARRAY_SELECTS,
273+
SQL_PROCEDURE_TERM,
274+
SQL_POSITIONED_STATEMENTS,
275+
SQL_GROUP_BY,
276+
SQL_OJ_CAPABILITIES,
277+
SQL_ORDER_BY_COLUMNS_IN_SELECT,
278+
SQL_OUTER_JOINS,
279+
SQL_QUOTED_IDENTIFIER_CASE,
280+
SQL_CONCAT_NULL_BEHAVIOR,
281+
SQL_NULL_COLLATION,
282+
SQL_ALTER_TABLE,
283+
SQL_UNION,
284+
SQL_DDL_INDEX,
285+
SQL_MULT_RESULT_SETS,
286+
SQL_OWNER_USAGE,
287+
SQL_QUALIFIER_USAGE,
288+
SQL_TIMEDATE_ADD_INTERVALS,
289+
SQL_TIMEDATE_DIFF_INTERVALS,
290+
SQL_IC_UPPER,
291+
SQL_IC_LOWER,
292+
SQL_IC_SENSITIVE,
293+
SQL_IC_MIXED,
294+
)
213295

214296

215297
def pooling(max_size: int = 100, idle_timeout: int = 600, enabled: bool = True) -> None:
@@ -249,81 +331,6 @@ def _custom_setattr(name, value):
249331
sys.modules[__name__].__setattr__ = _custom_setattr
250332

251333

252-
# Export SQL constants at module level
253-
SQL_VARCHAR: int = ConstantsDDBC.SQL_VARCHAR.value
254-
SQL_LONGVARCHAR: int = ConstantsDDBC.SQL_LONGVARCHAR.value
255-
SQL_WVARCHAR: int = ConstantsDDBC.SQL_WVARCHAR.value
256-
SQL_WLONGVARCHAR: int = ConstantsDDBC.SQL_WLONGVARCHAR.value
257-
SQL_DECIMAL: int = ConstantsDDBC.SQL_DECIMAL.value
258-
SQL_NUMERIC: int = ConstantsDDBC.SQL_NUMERIC.value
259-
SQL_BIT: int = ConstantsDDBC.SQL_BIT.value
260-
SQL_TINYINT: int = ConstantsDDBC.SQL_TINYINT.value
261-
SQL_SMALLINT: int = ConstantsDDBC.SQL_SMALLINT.value
262-
SQL_INTEGER: int = ConstantsDDBC.SQL_INTEGER.value
263-
SQL_BIGINT: int = ConstantsDDBC.SQL_BIGINT.value
264-
SQL_REAL: int = ConstantsDDBC.SQL_REAL.value
265-
SQL_FLOAT: int = ConstantsDDBC.SQL_FLOAT.value
266-
SQL_DOUBLE: int = ConstantsDDBC.SQL_DOUBLE.value
267-
SQL_BINARY: int = ConstantsDDBC.SQL_BINARY.value
268-
SQL_VARBINARY: int = ConstantsDDBC.SQL_VARBINARY.value
269-
SQL_LONGVARBINARY: int = ConstantsDDBC.SQL_LONGVARBINARY.value
270-
SQL_DATE: int = ConstantsDDBC.SQL_DATE.value
271-
SQL_TIME: int = ConstantsDDBC.SQL_TIME.value
272-
SQL_TIMESTAMP: int = ConstantsDDBC.SQL_TIMESTAMP.value
273-
274-
# Export GetInfo constants at module level
275-
# Driver and database information
276-
SQL_DRIVER_NAME: int = GetInfoConstants.SQL_DRIVER_NAME.value
277-
SQL_DRIVER_VER: int = GetInfoConstants.SQL_DRIVER_VER.value
278-
SQL_DRIVER_ODBC_VER: int = GetInfoConstants.SQL_DRIVER_ODBC_VER.value
279-
SQL_DATA_SOURCE_NAME: int = GetInfoConstants.SQL_DATA_SOURCE_NAME.value
280-
SQL_DATABASE_NAME: int = GetInfoConstants.SQL_DATABASE_NAME.value
281-
SQL_SERVER_NAME: int = GetInfoConstants.SQL_SERVER_NAME.value
282-
SQL_USER_NAME: int = GetInfoConstants.SQL_USER_NAME.value
283-
284-
# SQL conformance and support
285-
SQL_SQL_CONFORMANCE: int = GetInfoConstants.SQL_SQL_CONFORMANCE.value
286-
SQL_KEYWORDS: int = GetInfoConstants.SQL_KEYWORDS.value
287-
SQL_IDENTIFIER_QUOTE_CHAR: int = GetInfoConstants.SQL_IDENTIFIER_QUOTE_CHAR.value
288-
SQL_SEARCH_PATTERN_ESCAPE: int = GetInfoConstants.SQL_SEARCH_PATTERN_ESCAPE.value
289-
290-
# Catalog and schema support
291-
SQL_CATALOG_TERM: int = GetInfoConstants.SQL_CATALOG_TERM.value
292-
SQL_SCHEMA_TERM: int = GetInfoConstants.SQL_SCHEMA_TERM.value
293-
SQL_TABLE_TERM: int = GetInfoConstants.SQL_TABLE_TERM.value
294-
SQL_PROCEDURE_TERM: int = GetInfoConstants.SQL_PROCEDURE_TERM.value
295-
296-
# Transaction support
297-
SQL_TXN_CAPABLE: int = GetInfoConstants.SQL_TXN_CAPABLE.value
298-
SQL_DEFAULT_TXN_ISOLATION: int = GetInfoConstants.SQL_DEFAULT_TXN_ISOLATION.value
299-
300-
# Data type support
301-
SQL_NUMERIC_FUNCTIONS: int = GetInfoConstants.SQL_NUMERIC_FUNCTIONS.value
302-
SQL_STRING_FUNCTIONS: int = GetInfoConstants.SQL_STRING_FUNCTIONS.value
303-
SQL_DATETIME_FUNCTIONS: int = GetInfoConstants.SQL_DATETIME_FUNCTIONS.value
304-
305-
# Limits
306-
SQL_MAX_COLUMN_NAME_LEN: int = GetInfoConstants.SQL_MAX_COLUMN_NAME_LEN.value
307-
SQL_MAX_TABLE_NAME_LEN: int = GetInfoConstants.SQL_MAX_TABLE_NAME_LEN.value
308-
SQL_MAX_SCHEMA_NAME_LEN: int = GetInfoConstants.SQL_MAX_SCHEMA_NAME_LEN.value
309-
SQL_MAX_CATALOG_NAME_LEN: int = GetInfoConstants.SQL_MAX_CATALOG_NAME_LEN.value
310-
SQL_MAX_IDENTIFIER_LEN: int = GetInfoConstants.SQL_MAX_IDENTIFIER_LEN.value
311-
312-
313-
# Also provide a function to get all constants
314-
def get_info_constants() -> Dict[str, int]:
315-
"""
316-
Returns a dictionary of all available GetInfo constants.
317-
318-
This provides all SQLGetInfo constants that can be used with the Connection.getinfo() method
319-
to retrieve metadata about the database server and driver.
320-
321-
Returns:
322-
dict: Dictionary mapping constant names to their integer values
323-
"""
324-
return {name: member.value for name, member in GetInfoConstants.__members__.items()}
325-
326-
327334
# Create a custom module class that uses properties instead of __setattr__
328335
class _MSSQLModule(types.ModuleType):
329336
@property

0 commit comments

Comments
 (0)