diff --git a/DESCRIPTION.md b/DESCRIPTION.md index 58c2dfe2..909d52cf 100644 --- a/DESCRIPTION.md +++ b/DESCRIPTION.md @@ -14,6 +14,8 @@ Source code is also available at: - Add support for dynamic tables and required options - Add support for hybrid tables - Fixed SAWarning when registering functions with existing name in default namespace + - Update options to be defined in key arguments instead of arguments. + - Add support for refresh_mode option in DynamicTable - v1.6.1(July 9, 2024) diff --git a/src/snowflake/sqlalchemy/__init__.py b/src/snowflake/sqlalchemy/__init__.py index dedbab1e..e53f9b74 100644 --- a/src/snowflake/sqlalchemy/__init__.py +++ b/src/snowflake/sqlalchemy/__init__.py @@ -129,7 +129,7 @@ # Custom Tables "HybridTable", "DynamicTable", - # Custom Schema Options + # Custom Table Options "AsQueryOption", "TargetLagOption", "LiteralOption", diff --git a/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py b/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py index b75dc7bf..671c6957 100644 --- a/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py +++ b/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py @@ -50,13 +50,6 @@ def __init__( if not kw.get("autoload_with", False): self._validate_table() - def _append_parameter_error( - self, parameter: str, expected_argument: str, current_argument: str - ) -> None: - if not hasattr(self, "_parameter_error"): - self._parameter_error = [] - self._parameter_error.append((parameter, expected_argument, current_argument)) - def _validate_table(self): exceptions: List[Exception] = [] diff --git a/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py b/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py index 70adb4a9..93994abc 100644 --- a/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py +++ b/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py @@ -12,11 +12,10 @@ class AsQueryOption(TableOption): """Class to represent an AS clause in tables. - This configuration option is used to specify the query from which the table is created. For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-table#create-table-as-select-also-referred-to-as-ctas Example: - as_query=AsQuery('select name, address from existing_table where name = "test"') + as_query=AsQueryOption('select name, address from existing_table where name = "test"') is equivalent to: @@ -32,9 +31,9 @@ def __init__(self, query: Union[str, Selectable]) -> None: def create( value: Optional[Union["AsQueryOption", str, Selectable]] ) -> "TableOption": - if isinstance(value, NoneType) or isinstance(value, AsQueryOption): + if isinstance(value, (NoneType, AsQueryOption)): return value - if isinstance(value, str) or isinstance(value, Selectable): + if isinstance(value, (str, Selectable)): return AsQueryOption(value) return TableOption._get_invalid_table_option( TableOptionKey.AS_QUERY, @@ -58,7 +57,7 @@ def _render(self, compiler) -> str: return self.template() % (self.__get_expression()) def __repr__(self) -> str: - return "AsQuery(%s)" % self.__get_expression() + return "AsQueryOption(%s)" % self.__get_expression() AsQueryOptionType = Union[AsQueryOption, str, Selectable] diff --git a/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py b/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py index dad34cbe..b296898b 100644 --- a/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py +++ b/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py @@ -12,7 +12,7 @@ class IdentifierOption(TableOption): """Class to represent an identifier option in Snowflake Tables. Example: - warehouse = Identifier('my_warehouse') + warehouse = IdentifierOption('my_warehouse') is equivalent to: diff --git a/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py b/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py index 391dc5c5..ff6b444d 100644 --- a/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py +++ b/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py @@ -13,7 +13,7 @@ class KeywordOption(TableOption): """Class to represent a keyword option in Snowflake Tables. Example: - target_lag = Keyword(SnowflakeKeyword.DOWNSTREAM) + target_lag = KeywordOption(SnowflakeKeyword.DOWNSTREAM) is equivalent to: diff --git a/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py b/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py index de15473d..55dd7675 100644 --- a/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py +++ b/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py @@ -12,7 +12,7 @@ class LiteralOption(TableOption): """Class to represent a literal option in Snowflake Table. Example: - warehouse = Literal('my_warehouse') + warehouse = LiteralOption('my_warehouse') is equivalent to: diff --git a/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py b/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py index 2088c729..7c1c0825 100644 --- a/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py +++ b/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py @@ -19,14 +19,12 @@ class TimeUnit(Enum): class TargetLagOption(TableOption): - """Class to represent the target lag clause. - This configuration option is used to specify the target lag time for the dynamic table. + """Class to represent the target lag clause in Dynamic Tables. For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table - Example using the time and unit parameters: - target_lag = TargetLag(10, TimeUnit.SECONDS) + target_lag = TargetLagOption(10, TimeUnit.SECONDS) is equivalent to: @@ -90,7 +88,7 @@ def _render(self, compiler) -> str: return self.template() % (self.__get_expression()) def __repr__(self) -> str: - return "TargetLag(%s)" % self.__get_expression() + return "TargetLagOption(%s)" % self.__get_expression() TargetLagOptionType = Union[TargetLagOption, Tuple[int, TimeUnit]] diff --git a/tests/custom_tables/__snapshots__/test_generic_options.ambr b/tests/custom_tables/__snapshots__/test_generic_options.ambr index fe84351a..eef5e6fd 100644 --- a/tests/custom_tables/__snapshots__/test_generic_options.ambr +++ b/tests/custom_tables/__snapshots__/test_generic_options.ambr @@ -1,13 +1,13 @@ # serializer version: 1 # name: test_identifier_option_with_wrong_type - OptionKeyNotProvidedError('Expected option key in IdentifierOption option but got NoneType instead.') + InvalidTableParameterTypeError("Invalid parameter type 'int' provided for 'warehouse'. Expected one of the following types: 'IdentifierOption', 'str'.\n") # --- # name: test_identifier_option_without_name OptionKeyNotProvidedError('Expected option key in IdentifierOption option but got NoneType instead.') # --- # name: test_invalid_as_query_option - OptionKeyNotProvidedError('Expected option key in IdentifierOption option but got NoneType instead.') + InvalidTableParameterTypeError("Invalid parameter type 'int' provided for 'as_query'. Expected one of the following types: 'AsQueryOption', 'str', 'Selectable'.\n") # --- # name: test_literal_option_with_wrong_type - OptionKeyNotProvidedError('Expected option key in LiteralOption option but got NoneType instead.') + InvalidTableParameterTypeError("Invalid parameter type 'SnowflakeKeyword' provided for 'warehouse'. Expected one of the following types: 'LiteralOption', 'str', 'int'.\n") # --- diff --git a/tests/custom_tables/test_generic_options.py b/tests/custom_tables/test_generic_options.py index 040d1c25..916b94c6 100644 --- a/tests/custom_tables/test_generic_options.py +++ b/tests/custom_tables/test_generic_options.py @@ -8,6 +8,7 @@ IdentifierOption, KeywordOption, LiteralOption, + SnowflakeKeyword, TableOptionKey, TargetLagOption, exc, @@ -35,23 +36,25 @@ def test_identifier_option_without_name(snapshot): def test_identifier_option_with_wrong_type(snapshot): - identifier = IdentifierOption(23) - with pytest.raises(exc.OptionKeyNotProvidedError) as exc_info: + identifier = IdentifierOption.create(TableOptionKey.WAREHOUSE, 23) + with pytest.raises(exc.InvalidTableParameterTypeError) as exc_info: identifier.render_option(None) assert exc_info.value == snapshot def test_literal_option_with_wrong_type(snapshot): - literal = LiteralOption(0.32) - with pytest.raises(exc.OptionKeyNotProvidedError) as exc_info: + literal = LiteralOption.create( + TableOptionKey.WAREHOUSE, SnowflakeKeyword.DOWNSTREAM + ) + with pytest.raises(exc.InvalidTableParameterTypeError) as exc_info: literal.render_option(None) assert exc_info.value == snapshot def test_invalid_as_query_option(snapshot): - identifier = IdentifierOption(23) - with pytest.raises(exc.OptionKeyNotProvidedError) as exc_info: - identifier.render_option(None) + as_query = AsQueryOption.create(23) + with pytest.raises(exc.InvalidTableParameterTypeError) as exc_info: + as_query.render_option(None) assert exc_info.value == snapshot