diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d08e57..2a7239e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,11 @@ release (0.9.0), unrecognized arguments/keywords for these methods of creating a instead of being passed as ClickHouse server settings. This is in conjunction with some refactoring in Client construction. The supported method of passing ClickHouse server settings is to prefix such arguments/query parameters with`ch_`. +## 1.0.0 + +### Breaking Changes +- Removed the deprecated `utc_tz_aware` parameter entirely. Use `tz_mode` instead: `"naive_utc"` (default, was `False`), `"aware"` (was `True`), or `"schema"` (unchanged). Closes [#654](https://github.com/ClickHouse/clickhouse-connect/issues/654), [#665](https://github.com/ClickHouse/clickhouse-connect/issues/665) + ## UNRELEASED ### Breaking Changes diff --git a/clickhouse_connect/driver/__init__.py b/clickhouse_connect/driver/__init__.py index f2d384a2..5106aa71 100644 --- a/clickhouse_connect/driver/__init__.py +++ b/clickhouse_connect/driver/__init__.py @@ -82,7 +82,6 @@ def create_client(*, naive UTC timestamps. "aware" forces timezone-aware UTC datetimes. "schema" returns datetimes that match the server's column definition which means timezone-aware when the column defines a timezone and naive for bare DateTime columns. - :param utc_tz_aware Deprecated. Use tz_mode instead. :param autogenerate_session_id If set, this will override the 'autogenerate_session_id' common setting. :param form_encode_query_params If True, query parameters will be sent as form-encoded data in the request body instead of as URL parameters. This is useful for queries with large parameter sets that might exceed URL length @@ -212,7 +211,6 @@ async def create_async_client(*, naive UTC timestamps. "aware" forces timezone-aware UTC datetimes. "schema" returns datetimes that match the server's column definition which means timezone-aware when the column defines a timezone and naive for bare DateTime columns. - :param utc_tz_aware Deprecated. Use tz_mode instead. :param autogenerate_session_id If set, this will override the 'autogenerate_session_id' common setting. :param form_encode_query_params If True, query parameters will be sent as form-encoded data in the request body instead of as URL parameters. This is useful for queries with large parameter sets that might exceed URL length diff --git a/clickhouse_connect/driver/asyncclient.py b/clickhouse_connect/driver/asyncclient.py index 3f046042..8093955b 100644 --- a/clickhouse_connect/driver/asyncclient.py +++ b/clickhouse_connect/driver/asyncclient.py @@ -4,7 +4,7 @@ import os from concurrent.futures.thread import ThreadPoolExecutor from datetime import tzinfo -from typing import Literal, Optional, Union, Dict, Any, Sequence, Iterable, Generator, BinaryIO, TYPE_CHECKING +from typing import Optional, Union, Dict, Any, Sequence, Iterable, Generator, BinaryIO, TYPE_CHECKING from clickhouse_connect.driver.client import Client from clickhouse_connect.driver.query import TzMode @@ -116,7 +116,6 @@ async def query(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None) -> QueryResult: @@ -131,7 +130,7 @@ def _query(): column_formats=column_formats, encoding=encoding, use_none=use_none, column_oriented=column_oriented, use_numpy=use_numpy, max_str_len=max_str_len, context=context, query_tz=query_tz, column_tzs=column_tzs, - tz_mode=tz_mode, utc_tz_aware=utc_tz_aware, + tz_mode=tz_mode, external_data=external_data, transport_settings=transport_settings) loop = asyncio.get_running_loop() @@ -149,7 +148,6 @@ async def query_column_block_stream(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None, @@ -165,7 +163,7 @@ def _query_column_block_stream(): query_formats=query_formats, column_formats=column_formats, encoding=encoding, use_none=use_none, context=context, query_tz=query_tz, column_tzs=column_tzs, - tz_mode=tz_mode, utc_tz_aware=utc_tz_aware, + tz_mode=tz_mode, external_data=external_data, transport_settings=transport_settings) loop = asyncio.get_running_loop() @@ -183,7 +181,6 @@ async def query_row_block_stream(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None) -> StreamContext: @@ -198,7 +195,7 @@ def _query_row_block_stream(): query_formats=query_formats, column_formats=column_formats, encoding=encoding, use_none=use_none, context=context, query_tz=query_tz, column_tzs=column_tzs, - tz_mode=tz_mode, utc_tz_aware=utc_tz_aware, + tz_mode=tz_mode, external_data=external_data, transport_settings=transport_settings) loop = asyncio.get_running_loop() @@ -216,7 +213,6 @@ async def query_rows_stream(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None) -> StreamContext: @@ -231,7 +227,7 @@ def _query_rows_stream(): query_formats=query_formats, column_formats=column_formats, encoding=encoding, use_none=use_none, context=context, query_tz=query_tz, column_tzs=column_tzs, - tz_mode=tz_mode, utc_tz_aware=utc_tz_aware, + tz_mode=tz_mode, external_data=external_data, transport_settings=transport_settings) loop = asyncio.get_running_loop() @@ -364,7 +360,6 @@ async def query_df(self, use_na_values: Optional[bool] = None, query_tz: Optional[str] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, context: QueryContext = None, external_data: Optional[ExternalData] = None, use_extended_dtypes: Optional[bool] = None, @@ -381,7 +376,7 @@ def _query_df(): query_formats=query_formats, column_formats=column_formats, encoding=encoding, use_none=use_none, max_str_len=max_str_len, use_na_values=use_na_values, query_tz=query_tz, column_tzs=column_tzs, tz_mode=tz_mode, - utc_tz_aware=utc_tz_aware, context=context, + context=context, external_data=external_data, use_extended_dtypes=use_extended_dtypes, transport_settings=transport_settings) @@ -441,7 +436,6 @@ async def query_df_stream(self, use_na_values: Optional[bool] = None, query_tz: Optional[str] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, context: QueryContext = None, external_data: Optional[ExternalData] = None, use_extended_dtypes: Optional[bool] = None, @@ -459,7 +453,7 @@ def _query_df_stream(): encoding=encoding, use_none=use_none, max_str_len=max_str_len, use_na_values=use_na_values, query_tz=query_tz, column_tzs=column_tzs, - tz_mode=tz_mode, utc_tz_aware=utc_tz_aware, context=context, + tz_mode=tz_mode, context=context, external_data=external_data, use_extended_dtypes=use_extended_dtypes, transport_settings=transport_settings) @@ -526,8 +520,7 @@ def create_query_context(self, as_pandas: bool = False, external_data: Optional[ExternalData] = None, use_extended_dtypes: Optional[bool] = None, - transport_settings: Optional[Dict[str, str]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None) -> QueryContext: + transport_settings: Optional[Dict[str, str]] = None) -> QueryContext: """ Creates or updates a reusable QueryContext object :param query: Query statement/format string @@ -567,7 +560,7 @@ def create_query_context(self, column_oriented=column_oriented, use_numpy=use_numpy, max_str_len=max_str_len, context=context, query_tz=query_tz, column_tzs=column_tzs, - tz_mode=tz_mode, utc_tz_aware=utc_tz_aware, + tz_mode=tz_mode, use_na_values=use_na_values, streaming=streaming, as_pandas=as_pandas, external_data=external_data, diff --git a/clickhouse_connect/driver/client.py b/clickhouse_connect/driver/client.py index ef3a74bc..73387b8b 100644 --- a/clickhouse_connect/driver/client.py +++ b/clickhouse_connect/driver/client.py @@ -1,12 +1,11 @@ import io import logging -import warnings from datetime import tzinfo import pytz from abc import ABC, abstractmethod -from typing import Iterable, Literal, Optional, Any, Union, Sequence, Dict, Generator, BinaryIO, TYPE_CHECKING +from typing import Iterable, Optional, Any, Union, Sequence, Dict, Generator, BinaryIO, TYPE_CHECKING from pytz.exceptions import UnknownTimeZoneError from clickhouse_connect import common @@ -24,7 +23,7 @@ from clickhouse_connect.driver.summary import QuerySummary from clickhouse_connect.driver.models import ColumnDef, SettingDef, SettingStatus from clickhouse_connect.driver.query import QueryResult, to_arrow, to_arrow_batches, QueryContext, arrow_buffer, \ - TzMode, _resolve_tz_mode, _TZ_MODE_TO_UTC_TZ_AWARE + TzMode, _VALID_TZ_MODES from clickhouse_connect.driver.binding import quote_identifier if TYPE_CHECKING: @@ -41,11 +40,11 @@ def _strip_utc_timezone_from_arrow(table: "arrow.Table") -> "arrow.Table": """Strip UTC timezone from timestamp columns in Arrow table. This ensures naive datetimes are returned when the server timezone is UTC - and utc_tz_aware is False (the default). + and tz_mode is 'naive_utc' (the default). Only UTC-equivalent timezones (UTC, Etc/UTC, GMT, etc.) are stripped. Non-UTC timezones carry important offset information and are always - preserved regardless of utc_tz_aware setting. + preserved regardless of tz_mode setting. """ new_fields = [] needs_cast = False @@ -96,17 +95,6 @@ class Client(ABC): tz_mode: TzMode = "naive_utc" show_clickhouse_errors = True - @property - def utc_tz_aware(self) -> Union[bool, Literal["schema"]]: - """Deprecated: use tz_mode instead.""" - warnings.warn( - "utc_tz_aware is deprecated and will be removed in 1.0. " - "Use tz_mode instead.", - DeprecationWarning, - stacklevel=2, - ) - return _TZ_MODE_TO_UTC_TZ_AWARE[self.tz_mode] - def __init__(self, database: str, query_limit: int, @@ -115,8 +103,7 @@ def __init__(self, server_host_name: Optional[str], apply_server_timezone: Optional[Union[str, bool]], tz_mode: Optional[TzMode] = None, - show_clickhouse_errors: Optional[bool] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None): + show_clickhouse_errors: Optional[bool] = None): """ Shared initialization of ClickHouse Connect client :param database: database name @@ -126,7 +113,6 @@ def __init__(self, naive UTC timestamps. "aware" forces timezone-aware UTC datetimes. "schema" returns datetimes that match the server's column definition which means timezone-aware when the column defines a timezone and naive for bare DateTime columns. - :param utc_tz_aware: Deprecated. Use tz_mode instead. """ self.query_limit = coerce_int(query_limit) self.query_retries = coerce_int(query_retries) @@ -136,7 +122,9 @@ def __init__(self, self.show_clickhouse_errors = coerce_bool(show_clickhouse_errors) self.server_host_name = server_host_name self.uri = uri - self.tz_mode = _resolve_tz_mode(tz_mode, utc_tz_aware) + self.tz_mode = tz_mode if tz_mode is not None else "naive_utc" + if self.tz_mode not in _VALID_TZ_MODES: + raise ProgrammingError(f'tz_mode must be "naive_utc", "aware", or "schema", got "{self.tz_mode}"') self._init_common_settings(apply_server_timezone) def _init_common_settings(self, apply_server_timezone: Optional[Union[str, bool]]): @@ -285,7 +273,6 @@ def query(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None) -> QueryResult: @@ -322,7 +309,6 @@ def query_column_block_stream(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None) -> StreamContext: @@ -344,7 +330,6 @@ def query_row_block_stream(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None) -> StreamContext: @@ -366,7 +351,6 @@ def query_rows_stream(self, context: QueryContext = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, external_data: Optional[ExternalData] = None, transport_settings: Optional[Dict[str, str]] = None, tz_mode: Optional[TzMode] = None) -> StreamContext: @@ -476,7 +460,6 @@ def query_df(self, use_na_values: Optional[bool] = None, query_tz: Optional[str] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, context: QueryContext = None, external_data: Optional[ExternalData] = None, use_extended_dtypes: Optional[bool] = None, @@ -504,7 +487,6 @@ def query_df_stream(self, use_na_values: Optional[bool] = None, query_tz: Optional[str] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, context: QueryContext = None, external_data: Optional[ExternalData] = None, use_extended_dtypes: Optional[bool] = None, @@ -535,7 +517,6 @@ def create_query_context(self, context: Optional[QueryContext] = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, use_na_values: Optional[bool] = None, streaming: bool = False, as_pandas: bool = False, @@ -568,7 +549,6 @@ def create_query_context(self, :param tz_mode: Override the client default for handling UTC results. "aware" forces timezone-aware UTC datetimes, "naive_utc" returns naive UTC datetimes, and "schema" returns datetimes matching the server's column definition. - :param utc_tz_aware: Deprecated. Use tz_mode instead. :param use_na_values: Deprecated alias for use_advanced_dtypes :param as_pandas Return the result columns as pandas.Series objects :param streaming Marker used to correctly configure streaming queries @@ -579,10 +559,7 @@ def create_query_context(self, :param transport_settings: Optional dictionary of transport level settings (HTTP headers, etc.) :return: Reusable QueryContext """ - if tz_mode is not None or utc_tz_aware is not None: - resolved_tz_mode = _resolve_tz_mode(tz_mode, utc_tz_aware) - else: - resolved_tz_mode = self.tz_mode + resolved_tz_mode = tz_mode if tz_mode is not None else self.tz_mode if context: return context.updated_copy(query=query, parameters=parameters, diff --git a/clickhouse_connect/driver/httpclient.py b/clickhouse_connect/driver/httpclient.py index f4760f2c..3f08adcc 100644 --- a/clickhouse_connect/driver/httpclient.py +++ b/clickhouse_connect/driver/httpclient.py @@ -6,7 +6,7 @@ from importlib import import_module from importlib.metadata import version as dist_version from base64 import b64encode -from typing import Literal, Optional, Dict, Any, Sequence, Union, List, Callable, Generator, BinaryIO +from typing import Optional, Dict, Any, Sequence, Union, List, Callable, Generator, BinaryIO from urllib.parse import urlencode from urllib3 import Timeout @@ -78,7 +78,6 @@ def __init__(self, server_host_name: Optional[str] = None, apply_server_timezone: Optional[Union[str, bool]] = None, tz_mode: Optional[str] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, show_clickhouse_errors: Optional[bool] = None, autogenerate_session_id: Optional[bool] = None, autogenerate_query_id: Optional[bool] = None, @@ -187,7 +186,6 @@ def __init__(self, server_host_name=server_host_name, apply_server_timezone=apply_server_timezone, tz_mode=tz_mode, - utc_tz_aware=utc_tz_aware, show_clickhouse_errors=show_clickhouse_errors) self.params = dict_copy(self.params, self._validate_settings(ch_settings)) cancel_setting = self._setting_status("cancel_http_readonly_queries_on_client_close") diff --git a/clickhouse_connect/driver/query.py b/clickhouse_connect/driver/query.py index a047441e..f1941d41 100644 --- a/clickhouse_connect/driver/query.py +++ b/clickhouse_connect/driver/query.py @@ -1,6 +1,6 @@ import logging import re -import warnings + import pytz from io import IOBase @@ -25,65 +25,8 @@ TzMode = Literal["naive_utc", "aware", "schema"] -_UTC_TZ_AWARE_TO_TZ_MODE: Dict[Union[bool, str], TzMode] = { - False: "naive_utc", - True: "aware", - "schema": "schema", -} - _VALID_TZ_MODES = {"naive_utc", "aware", "schema"} -_TZ_MODE_TO_UTC_TZ_AWARE: Dict[str, Union[bool, Literal["schema"]]] = { - "naive_utc": False, - "aware": True, - "schema": "schema", -} - -# Mapping for string booleans that may arrive via URL params -_STR_BOOL_MAP = {"true": True, "false": False, "1": True, "0": False} - - -def _resolve_tz_mode( - tz_mode: Optional[TzMode] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, -) -> TzMode: - """Resolve tz_mode from either the new ``tz_mode`` or deprecated ``utc_tz_aware`` parameter. - - Returns the canonical TzMode string. Raises ``ProgrammingError`` on conflicts or - invalid values. - """ - if tz_mode is not None and utc_tz_aware is not None: - raise ProgrammingError( - "Cannot specify both 'tz_mode' and 'utc_tz_aware'. " - "Use 'tz_mode' only; 'utc_tz_aware' is deprecated." - ) - - if utc_tz_aware is not None: - # Coerce string booleans from URL params (e.g. "true" -> True) - if isinstance(utc_tz_aware, str) and utc_tz_aware.lower() in _STR_BOOL_MAP: - utc_tz_aware = _STR_BOOL_MAP[utc_tz_aware.lower()] - - if utc_tz_aware not in _UTC_TZ_AWARE_TO_TZ_MODE: - raise ProgrammingError( - f'utc_tz_aware must be True, False, or "schema", got "{utc_tz_aware}"' - ) - warnings.warn( - "utc_tz_aware is deprecated and will be removed in 1.0. " - "Use tz_mode='naive_utc' | 'aware' | 'schema' instead.", - DeprecationWarning, - stacklevel=3, - ) - return _UTC_TZ_AWARE_TO_TZ_MODE[utc_tz_aware] - - if tz_mode is not None: - if tz_mode not in _VALID_TZ_MODES: - raise ProgrammingError( - f'tz_mode must be "naive_utc", "aware", or "schema", got "{tz_mode}"' - ) - return tz_mode - - return "naive_utc" - commands = 'CREATE|ALTER|SYSTEM|GRANT|REVOKE|CHECK|DETACH|ATTACH|DROP|DELETE|KILL|' + \ 'OPTIMIZE|SET|RENAME|TRUNCATE|USE|UPDATE' @@ -115,7 +58,6 @@ def __init__(self, max_str_len: Optional[int] = 0, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, use_extended_dtypes: Optional[bool] = None, as_pandas: bool = False, streaming: bool = False, @@ -154,7 +96,6 @@ def __init__(self, naive UTC timestamps. "aware" forces timezone-aware UTC datetimes. "schema" returns datetimes that match the server's column definition which means timezone-aware when the column schema defines a timezone (e.g. DateTime('UTC')) and naive for bare DateTime columns. - :param utc_tz_aware Deprecated. Use tz_mode instead. """ super().__init__(settings, query_formats, @@ -172,7 +113,9 @@ def __init__(self, self.server_tz = server_tz self.apply_server_tz = apply_server_tz self.external_data = external_data - self.tz_mode = _resolve_tz_mode(tz_mode, utc_tz_aware) + self.tz_mode = tz_mode if tz_mode is not None else "naive_utc" + if self.tz_mode not in _VALID_TZ_MODES: + raise ProgrammingError(f'tz_mode must be "naive_utc", "aware", or "schema", got "{self.tz_mode}"') if isinstance(query_tz, str): try: query_tz = pytz.timezone(query_tz) @@ -223,17 +166,6 @@ def is_insert(self) -> bool: def is_command(self) -> bool: return command_re.search(self.uncommented_query) is not None - @property - def utc_tz_aware(self) -> Union[bool, Literal["schema"]]: - """Deprecated: use tz_mode instead.""" - warnings.warn( - "utc_tz_aware is deprecated and will be removed in 1.0. " - "Use tz_mode instead.", - DeprecationWarning, - stacklevel=2, - ) - return _TZ_MODE_TO_UTC_TZ_AWARE[self.tz_mode] - def set_parameters(self, parameters: Dict[str, Any]): self.parameters = parameters self._update_query() @@ -288,7 +220,6 @@ def updated_copy(self, max_str_len: Optional[int] = None, query_tz: Optional[Union[str, tzinfo]] = None, column_tzs: Optional[Dict[str, Union[str, tzinfo]]] = None, - utc_tz_aware: Optional[Union[bool, Literal["schema"]]] = None, use_extended_dtypes: Optional[bool] = None, as_pandas: bool = False, streaming: bool = False, @@ -299,10 +230,7 @@ def updated_copy(self, """ Creates Query context copy with parameters overridden/updated as appropriate. """ - if tz_mode is not None or utc_tz_aware is not None: - resolved_tz_mode = _resolve_tz_mode(tz_mode, utc_tz_aware) - else: - resolved_tz_mode = self.tz_mode + resolved_tz_mode = tz_mode if tz_mode is not None else self.tz_mode return QueryContext( query=query or self.query, parameters=dict_copy(self.parameters, parameters), diff --git a/tests/unit_tests/test_driver/test_query.py b/tests/unit_tests/test_driver/test_query.py index 1ae7e08b..6c635bed 100644 --- a/tests/unit_tests/test_driver/test_query.py +++ b/tests/unit_tests/test_driver/test_query.py @@ -1,12 +1,10 @@ -import warnings - import pytz import pytest import pyarrow as pa from clickhouse_connect.driver.exceptions import ProgrammingError -from clickhouse_connect.driver.query import QueryContext, _resolve_tz_mode +from clickhouse_connect.driver.query import QueryContext from clickhouse_connect.driver.client import _strip_utc_timezone_from_arrow from clickhouse_connect.driver import tzutil @@ -243,83 +241,3 @@ def test_tz_mode_invalid_string_raises(): """Invalid string value for tz_mode should raise ProgrammingError.""" with pytest.raises(ProgrammingError, match='tz_mode must be'): QueryContext(tz_mode="invalid") - - -def test_utc_tz_aware_false_maps_to_naive_utc(): - """utc_tz_aware=False should map to tz_mode='naive_utc' with a DeprecationWarning.""" - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - ctx = QueryContext(utc_tz_aware=False) - assert ctx.tz_mode == "naive_utc" - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "utc_tz_aware is deprecated" in str(w[0].message) - - -def test_utc_tz_aware_true_maps_to_aware(): - """utc_tz_aware=True should map to tz_mode='aware' with a DeprecationWarning.""" - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - ctx = QueryContext(utc_tz_aware=True) - assert ctx.tz_mode == "aware" - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - - -def test_utc_tz_aware_schema_maps_to_schema(): - """utc_tz_aware='schema' should map to tz_mode='schema' with a DeprecationWarning.""" - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - ctx = QueryContext(utc_tz_aware="schema") - assert ctx.tz_mode == "schema" - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - - -def test_both_tz_mode_and_utc_tz_aware_raises(): - """Providing both tz_mode and utc_tz_aware should raise ProgrammingError.""" - with pytest.raises(ProgrammingError, match='Cannot specify both'): - QueryContext(tz_mode="aware", utc_tz_aware=True) - - -def test_utc_tz_aware_invalid_string_raises(): - """Invalid string value for utc_tz_aware should raise ProgrammingError.""" - with pytest.raises(ProgrammingError, match='utc_tz_aware must be'): - QueryContext(utc_tz_aware="invalid") - - -def test_resolve_tz_mode_defaults(): - """No arguments should return 'naive_utc'.""" - assert _resolve_tz_mode() == "naive_utc" - - -def test_resolve_tz_mode_string_bool_coercion(): - """String booleans from URL params should be coerced correctly.""" - with warnings.catch_warnings(record=True): - warnings.simplefilter("always") - assert _resolve_tz_mode(utc_tz_aware="true") == "aware" - assert _resolve_tz_mode(utc_tz_aware="false") == "naive_utc" - assert _resolve_tz_mode(utc_tz_aware="True") == "aware" - assert _resolve_tz_mode(utc_tz_aware="False") == "naive_utc" - assert _resolve_tz_mode(utc_tz_aware="1") == "aware" - assert _resolve_tz_mode(utc_tz_aware="0") == "naive_utc" - - -def test_utc_tz_aware_property_returns_legacy_value(): - """Accessing ctx.utc_tz_aware should return the legacy equivalent with a DeprecationWarning.""" - ctx = QueryContext(tz_mode="naive_utc") - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - assert ctx.utc_tz_aware is False - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - - ctx2 = QueryContext(tz_mode="aware") - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - assert ctx2.utc_tz_aware is True - - ctx3 = QueryContext(tz_mode="schema") - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - assert ctx3.utc_tz_aware == "schema"