From 0f00101c4b85ea9641722ceab209e61ad53d42a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 12:09:00 +0000 Subject: [PATCH] fix(deps): update dependency sqlglot to >=23.4,<23.10 (#8787) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Co-authored-by: Gil Forsyth --- ibis/backends/bigquery/compiler.py | 25 ++------ ibis/backends/clickhouse/compiler.py | 2 - .../test_column_regexp_extract/out.sql | 4 +- .../test_column_regexp_replace/out.sql | 2 +- .../{cumulate_window => cumulate}/out.sql | 0 .../{hop_window => hop}/out.sql | 0 .../{tumble_window => tumble}/out.sql | 0 ibis/backends/flink/tests/test_compiler.py | 59 ++++++++----------- ibis/backends/impala/compiler.py | 16 ----- ibis/backends/mysql/compiler.py | 6 +- ibis/backends/postgres/compiler.py | 2 +- ibis/backends/pyspark/compiler.py | 3 - ibis/backends/snowflake/compiler.py | 3 - ibis/backends/sql/compiler.py | 7 ++- ibis/backends/sql/datatypes.py | 6 +- ibis/backends/sql/dialects.py | 4 +- poetry.lock | 10 ++-- pyproject.toml | 2 +- requirements-dev.txt | 2 +- 19 files changed, 57 insertions(+), 96 deletions(-) rename ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/{cumulate_window => cumulate}/out.sql (100%) rename ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/{hop_window => hop}/out.sql (100%) rename ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/{tumble_window => tumble}/out.sql (100%) diff --git a/ibis/backends/bigquery/compiler.py b/ibis/backends/bigquery/compiler.py index 24d7f2065b35..cfe8f27ac2e9 100644 --- a/ibis/backends/bigquery/compiler.py +++ b/ibis/backends/bigquery/compiler.py @@ -293,22 +293,7 @@ def visti_StringFind(self, op, *, arg, substr, start, end): return self.f.strpos(arg, substr) def visit_NonNullLiteral(self, op, *, value, dtype): - if dtype.is_string(): - return sge.convert( - str(value) - # Escape \ first so we don't double escape other characters. - .replace("\\", "\\\\") - # ASCII escape sequences that are recognized in Python: - # https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals - .replace("\a", "\\a") # Bell - .replace("\b", "\\b") # Backspace - .replace("\f", "\\f") # Formfeed - .replace("\n", "\\n") # Newline / Linefeed - .replace("\r", "\\r") # Carriage return - .replace("\t", "\\t") # Tab - .replace("\v", "\\v") # Vertical tab - ) - elif dtype.is_inet() or dtype.is_macaddr(): + if dtype.is_inet() or dtype.is_macaddr(): return sge.convert(str(value)) elif dtype.is_timestamp(): funcname = "datetime" if dtype.timezone is None else "timestamp" @@ -570,10 +555,10 @@ def visit_RegexExtract(self, op, *, arg, pattern, index): nonzero_index_replace = self.f.regexp_replace( arg, self.f.concat(".*?", pattern, ".*"), - self.f.concat("\\\\", self.cast(index, dt.string)), + self.f.concat("\\", self.cast(index, dt.string)), ) zero_index_replace = self.f.regexp_replace( - arg, self.f.concat(".*?", self.f.concat("(", pattern, ")"), ".*"), "\\\\1" + arg, self.f.concat(".*?", self.f.concat("(", pattern, ")"), ".*"), "\\1" ) extract = self.if_(index.eq(0), zero_index_replace, nonzero_index_replace) return self.if_(matches, extract, NULL) @@ -653,7 +638,7 @@ def visit_TypeOf(self, op, *, arg): self.if_(self.f.regexp_contains(name, "^-?[0-9]*$"), "INT64"), self.if_( self.f.regexp_contains( - name, r'^(-?[0-9]+[.e].*|CAST\\("([^"]*)" AS FLOAT64\\))$' + name, r'^(-?[0-9]+[.e].*|CAST\("([^"]*)" AS FLOAT64\))$' ), "FLOAT64", ), @@ -664,7 +649,7 @@ def visit_TypeOf(self, op, *, arg): ), self.if_(self.f.starts_with(name, 'b"'), "BYTES"), self.if_(self.f.starts_with(name, "["), "ARRAY"), - self.if_(self.f.regexp_contains(name, r"^(STRUCT)?\\("), "STRUCT"), + self.if_(self.f.regexp_contains(name, r"^(STRUCT)?\("), "STRUCT"), self.if_(self.f.starts_with(name, "ST_"), "GEOGRAPHY"), self.if_(name.eq(sge.convert("NULL")), "NULL"), ] diff --git a/ibis/backends/clickhouse/compiler.py b/ibis/backends/clickhouse/compiler.py index 5b079776328f..a99c4829d0b1 100644 --- a/ibis/backends/clickhouse/compiler.py +++ b/ibis/backends/clickhouse/compiler.py @@ -280,8 +280,6 @@ def visit_NonNullLiteral(self, op, *, value, dtype): if dtype.is_inet(): v = str(value) return self.f.toIPv6(v) if ":" in v else self.f.toIPv4(v) - elif dtype.is_string(): - return sge.convert(str(value).replace(r"\0", r"\\0")) elif dtype.is_decimal(): precision = dtype.precision if precision is None or not 1 <= precision <= 76: diff --git a/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_extract/out.sql b/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_extract/out.sql index 2da83aca651e..5e8d5f970a03 100644 --- a/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_extract/out.sql +++ b/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_extract/out.sql @@ -1,9 +1,9 @@ SELECT CASE WHEN notEmpty( - extractGroups(CAST("t0"."string_col" AS String), CONCAT('(', '[\d]+', ')'))[3 + 1] + extractGroups(CAST("t0"."string_col" AS String), CONCAT('(', '[\\d]+', ')'))[3 + 1] ) - THEN extractGroups(CAST("t0"."string_col" AS String), CONCAT('(', '[\d]+', ')'))[3 + 1] + THEN extractGroups(CAST("t0"."string_col" AS String), CONCAT('(', '[\\d]+', ')'))[3 + 1] ELSE NULL END AS "RegexExtract(string_col, '[\\d]+', 3)" FROM "functional_alltypes" AS "t0" \ No newline at end of file diff --git a/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_replace/out.sql b/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_replace/out.sql index 5eb21c433e57..66bd22f69db1 100644 --- a/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_replace/out.sql +++ b/ibis/backends/clickhouse/tests/snapshots/test_functions/test_column_regexp_replace/out.sql @@ -1,3 +1,3 @@ SELECT - replaceRegexpAll("t0"."string_col", '[\d]+', 'aaa') AS "RegexReplace(string_col, '[\\d]+', 'aaa')" + replaceRegexpAll("t0"."string_col", '[\\d]+', 'aaa') AS "RegexReplace(string_col, '[\\d]+', 'aaa')" FROM "functional_alltypes" AS "t0" \ No newline at end of file diff --git a/ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/cumulate_window/out.sql b/ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/cumulate/out.sql similarity index 100% rename from ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/cumulate_window/out.sql rename to ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/cumulate/out.sql diff --git a/ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/hop_window/out.sql b/ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/hop/out.sql similarity index 100% rename from ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/hop_window/out.sql rename to ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/hop/out.sql diff --git a/ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/tumble_window/out.sql b/ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/tumble/out.sql similarity index 100% rename from ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/tumble_window/out.sql rename to ibis/backends/flink/tests/snapshots/test_compiler/test_windowing_tvf/tumble/out.sql diff --git a/ibis/backends/flink/tests/test_compiler.py b/ibis/backends/flink/tests/test_compiler.py index 210ba6f9941e..cdfe9a996d92 100644 --- a/ibis/backends/flink/tests/test_compiler.py +++ b/ibis/backends/flink/tests/test_compiler.py @@ -1,5 +1,7 @@ from __future__ import annotations +from operator import methodcaller + import pytest from pytest import param @@ -7,12 +9,12 @@ from ibis.common.deferred import _ -def test_sum(con, simple_table, assert_sql): +def test_sum(simple_table, assert_sql): expr = simple_table.a.sum() assert_sql(expr) -def test_count_star(con, simple_table, assert_sql): +def test_count_star(simple_table, assert_sql): expr = simple_table.group_by(simple_table.i).size() assert_sql(expr) @@ -24,12 +26,12 @@ def test_count_star(con, simple_table, assert_sql): param("s", id="timestamp_s"), ], ) -def test_timestamp_from_unix(con, simple_table, unit, assert_sql): +def test_timestamp_from_unix(simple_table, unit, assert_sql): expr = simple_table.d.to_timestamp(unit=unit) assert_sql(expr) -def test_complex_projections(con, simple_table, assert_sql): +def test_complex_projections(simple_table, assert_sql): expr = ( simple_table.group_by(["a", "c"]) .aggregate(the_sum=simple_table.b.sum()) @@ -39,7 +41,7 @@ def test_complex_projections(con, simple_table, assert_sql): assert_sql(expr) -def test_filter(con, simple_table, assert_sql): +def test_filter(simple_table, assert_sql): expr = simple_table[ ((simple_table.c > 0) | (simple_table.c < 0)) & simple_table.g.isin(["A", "B"]) ] @@ -60,12 +62,12 @@ def test_filter(con, simple_table, assert_sql): "second", ], ) -def test_extract_fields(con, simple_table, kind, assert_sql): +def test_extract_fields(simple_table, kind, assert_sql): expr = getattr(simple_table.i, kind)().name("tmp") assert_sql(expr) -def test_complex_groupby_aggregation(con, simple_table, assert_sql): +def test_complex_groupby_aggregation(simple_table, assert_sql): keys = [simple_table.i.year().name("year"), simple_table.i.month().name("month")] b_unique = simple_table.b.nunique() expr = simple_table.group_by(keys).aggregate( @@ -74,12 +76,12 @@ def test_complex_groupby_aggregation(con, simple_table, assert_sql): assert_sql(expr) -def test_simple_filtered_agg(con, simple_table, assert_sql): +def test_simple_filtered_agg(simple_table, assert_sql): expr = simple_table.b.nunique(where=simple_table.g == "A") assert_sql(expr) -def test_complex_filtered_agg(con, snapshot, simple_table, assert_sql): +def test_complex_filtered_agg(simple_table, assert_sql): expr = simple_table.group_by("b").aggregate( total=simple_table.count(), avg_a=simple_table.a.mean(), @@ -89,12 +91,12 @@ def test_complex_filtered_agg(con, snapshot, simple_table, assert_sql): assert_sql(expr) -def test_value_counts(con, simple_table, assert_sql): +def test_value_counts(simple_table, assert_sql): expr = simple_table.i.year().value_counts() assert_sql(expr) -def test_having(con, simple_table, assert_sql): +def test_having(simple_table, assert_sql): expr = ( simple_table.group_by("g") .having(simple_table.count() >= 1000) @@ -104,37 +106,28 @@ def test_having(con, simple_table, assert_sql): @pytest.mark.parametrize( - "function_type,params", + "method", [ - pytest.param( - "tumble", {"window_size": ibis.interval(minutes=15)}, id="tumble_window" - ), - pytest.param( + methodcaller("tumble", window_size=ibis.interval(minutes=15)), + methodcaller( "hop", - { - "window_size": ibis.interval(minutes=15), - "window_slide": ibis.interval(minutes=1), - }, - id="hop_window", + window_size=ibis.interval(minutes=15), + window_slide=ibis.interval(minutes=1), ), - pytest.param( + methodcaller( "cumulate", - { - "window_size": ibis.interval(minutes=1), - "window_step": ibis.interval(seconds=10), - }, - id="cumulate_window", + window_size=ibis.interval(minutes=1), + window_step=ibis.interval(seconds=10), ), ], + ids=["tumble", "hop", "cumulate"], ) -def test_windowing_tvf(con, simple_table, function_type, params, assert_sql): - expr = getattr(simple_table.window_by(time_col=simple_table.i), function_type)( - **params - ) +def test_windowing_tvf(simple_table, method, assert_sql): + expr = method(simple_table.window_by(time_col=simple_table.i)) assert_sql(expr) -def test_window_aggregation(con, simple_table, assert_sql): +def test_window_aggregation(simple_table, assert_sql): expr = ( simple_table.window_by(time_col=simple_table.i) .tumble(window_size=ibis.interval(minutes=15)) @@ -144,7 +137,7 @@ def test_window_aggregation(con, simple_table, assert_sql): assert_sql(expr) -def test_window_topn(con, simple_table, assert_sql): +def test_window_topn(simple_table, assert_sql): expr = simple_table.window_by(time_col="i").tumble( window_size=ibis.interval(seconds=600), )["a", "b", "c", "d", "g", "window_start", "window_end"] diff --git a/ibis/backends/impala/compiler.py b/ibis/backends/impala/compiler.py index 9561319d0d2a..ad599197aa38 100644 --- a/ibis/backends/impala/compiler.py +++ b/ibis/backends/impala/compiler.py @@ -195,22 +195,6 @@ def visit_NonNullLiteral(self, op, *, value, dtype): # supported, but only within a certain range, and the # implementation wraps on over- and underflow return sge.convert(value.isoformat()) - elif dtype.is_string(): - value = ( - value - # Escape \ first so we don't double escape other characters. - .replace("\\", "\\\\") - # ASCII escape sequences that are recognized in Python: - # https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals - .replace("\a", "\\a") # Bell - .replace("\b", "\\b") # Backspace - .replace("\f", "\\f") # Formfeed - .replace("\n", "\\n") # Newline / Linefeed - .replace("\r", "\\r") # Carriage return - .replace("\t", "\\t") # Tab - .replace("\v", "\\v") # Vertical tab - ) - return sge.convert(value) elif dtype.is_decimal() and not value.is_finite(): raise com.UnsupportedOperationError( f"Non-finite decimal literal values are not supported by Impala; got: {value}" diff --git a/ibis/backends/mysql/compiler.py b/ibis/backends/mysql/compiler.py index 1717d446a768..ef636acde229 100644 --- a/ibis/backends/mysql/compiler.py +++ b/ibis/backends/mysql/compiler.py @@ -217,8 +217,6 @@ def visit_NonNullLiteral(self, op, *, value, dtype): raise com.UnsupportedBackendType( "MySQL does not support arrays, structs or maps" ) - elif dtype.is_string(): - return sge.convert(value.replace("\\", "\\\\")) return None def visit_JSONGetItem(self, op, *, arg, index): @@ -260,7 +258,7 @@ def visit_RegexExtract(self, op, *, arg, pattern, index): index.eq(0), extracted, self.f.regexp_replace( - extracted, pattern, rf"\\{index.sql(self.dialect)}" + extracted, pattern, f"\\{index.sql(self.dialect)}" ), ), NULL, @@ -336,7 +334,7 @@ def visit_RStrip(self, op, *, arg): return self.visit_LRStrip(op, arg=arg, position="TRAILING") def visit_IntervalFromInteger(self, op, *, arg, unit): - return sge.Interval(this=arg, unit=sge.convert(op.resolution.upper())) + return sge.Interval(this=arg, unit=sge.Var(this=op.resolution.upper())) def visit_TimestampAdd(self, op, *, left, right): if op.right.dtype.unit.short == "ms": diff --git a/ibis/backends/postgres/compiler.py b/ibis/backends/postgres/compiler.py index 659b5df271e5..d3b2034518b4 100644 --- a/ibis/backends/postgres/compiler.py +++ b/ibis/backends/postgres/compiler.py @@ -469,7 +469,7 @@ def visit_ExtractEpochSeconds(self, op, *, arg): def visit_ArrayIndex(self, op, *, arg, index): index = self.if_(index < 0, self.f.cardinality(arg) + index, index) - return sge.paren(arg, copy=False)[index + 1] + return sge.paren(arg, copy=False)[index] def visit_ArraySlice(self, op, *, arg, start, stop): neg_to_pos_index = lambda n, index: self.if_(index < 0, n + index, index) diff --git a/ibis/backends/pyspark/compiler.py b/ibis/backends/pyspark/compiler.py index c9fa54b87bc8..0793ce9d5d83 100644 --- a/ibis/backends/pyspark/compiler.py +++ b/ibis/backends/pyspark/compiler.py @@ -92,9 +92,6 @@ def visit_NonNullLiteral(self, op, *, value, dtype): return self.f.nanvl(result, NULL) else: return result - elif dtype.is_string(): - value = value.replace("\\", "\\\\") - return sge.convert(value) elif dtype.is_binary(): return self.f.unhex(value.hex()) elif dtype.is_decimal(): diff --git a/ibis/backends/snowflake/compiler.py b/ibis/backends/snowflake/compiler.py index 91537d0fd9a5..78061eaf6a1e 100644 --- a/ibis/backends/snowflake/compiler.py +++ b/ibis/backends/snowflake/compiler.py @@ -109,9 +109,6 @@ def _minimize_spec(start, end, spec): def visit_Literal(self, op, *, value, dtype): if value is None: return super().visit_Literal(op, value=value, dtype=dtype) - elif dtype.is_string(): - # sqlglot doesn't escape backslashes in strings - return sge.convert(value.replace("\\", "\\\\")) elif dtype.is_timestamp(): args = ( value.year, diff --git a/ibis/backends/sql/compiler.py b/ibis/backends/sql/compiler.py index b231c1c3e76f..ce425c1560eb 100644 --- a/ibis/backends/sql/compiler.py +++ b/ibis/backends/sql/compiler.py @@ -586,7 +586,8 @@ def visit_DefaultLiteral(self, op, *, value, dtype): return self.cast(str(value), dtype) elif dtype.is_interval(): return sge.Interval( - this=sge.convert(str(value)), unit=dtype.resolution.upper() + this=sge.convert(str(value)), + unit=sge.Var(this=dtype.resolution.upper()), ) elif dtype.is_boolean(): return sge.Boolean(this=bool(value)) @@ -788,7 +789,9 @@ def visit_DayOfWeekName(self, op, *, arg): ) def visit_IntervalFromInteger(self, op, *, arg, unit): - return sge.Interval(this=sge.convert(arg), unit=unit.singular.upper()) + return sge.Interval( + this=sge.convert(arg), unit=sge.Var(this=unit.singular.upper()) + ) ### String Instruments diff --git a/ibis/backends/sql/datatypes.py b/ibis/backends/sql/datatypes.py index 2fc709038887..83798738cf9e 100644 --- a/ibis/backends/sql/datatypes.py +++ b/ibis/backends/sql/datatypes.py @@ -164,7 +164,7 @@ def to_ibis(cls, typ: sge.DataType, nullable: bool | None = None) -> dt.DataType if isinstance(typecode, sge.Interval): typ = sge.DataType( this=sge.DataType.Type.INTERVAL, - expressions=[sge.IntervalSpan(this=typecode.unit)], + expressions=[typecode.unit], ) typecode = typ.this @@ -731,6 +731,10 @@ def _from_sqlglot_DATETIME(cls) -> dt.Timestamp: @classmethod def _from_sqlglot_TIMESTAMP(cls) -> dt.Timestamp: + return dt.Timestamp(timezone=None, nullable=cls.default_nullable) + + @classmethod + def _from_sqlglot_TIMESTAMPTZ(cls) -> dt.Timestamp: return dt.Timestamp(timezone="UTC", nullable=cls.default_nullable) @classmethod diff --git a/ibis/backends/sql/dialects.py b/ibis/backends/sql/dialects.py index f9c7a7e40573..b7166deeb90a 100644 --- a/ibis/backends/sql/dialects.py +++ b/ibis/backends/sql/dialects.py @@ -106,7 +106,7 @@ def _interval_with_precision(self, e): formatted_arg = f"'{formatted_arg}'" prec = _calculate_precision(int(arg)) prec = max(prec, 2) - unit += f"({prec})" + unit.args["this"] += f"({prec})" return f"INTERVAL {formatted_arg} {unit}" @@ -182,6 +182,8 @@ def new_name(names: set[str], name: str) -> str: class Flink(Hive): + UNESCAPED_SEQUENCES = {"\\\\d": "\\d"} + class Generator(Hive.Generator): UNNEST_WITH_ORDINALITY = False diff --git a/poetry.lock b/poetry.lock index c1f7187ba6f6..188eb5901450 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6408,18 +6408,18 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sqlglot" -version = "23.0.5" +version = "23.9.0" description = "An easily customizable SQL parser and transpiler" optional = false python-versions = ">=3.7" files = [ - {file = "sqlglot-23.0.5-py3-none-any.whl", hash = "sha256:12aadd50775209573e7dfa0136f5769da865de3d5954e8e88a02eb0081d1ad6b"}, - {file = "sqlglot-23.0.5.tar.gz", hash = "sha256:68386c5aebdc9a1c74e2a623f6373805a2f839d205d46945258dde5ac692d515"}, + {file = "sqlglot-23.9.0-py3-none-any.whl", hash = "sha256:066aac4246cd48f26b9cd4e882b18c69cdc26c7eddc4492a87a8c470b54872b2"}, + {file = "sqlglot-23.9.0.tar.gz", hash = "sha256:809db70a5c15a3d17dad2056ee5d90008945468a5bf12ec17c2df0d73b6f1752"}, ] [package.extras] dev = ["duckdb (>=0.6)", "maturin (>=1.4,<2.0)", "mypy", "pandas", "pandas-stubs", "pdoc", "pre-commit", "pyspark", "python-dateutil", "ruff (==0.3.2)", "types-python-dateutil", "typing-extensions"] -rs = ["sqlglotrs (==0.1.2)"] +rs = ["sqlglotrs (==0.2.0)"] [[package]] name = "stack-data" @@ -7358,4 +7358,4 @@ visualization = ["graphviz"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "1561446bc314ca0876476a8275f88045ac617b1c782066d78f2c922397011510" +content-hash = "f9df067e7837adbf693f52873978909e9cce8239685b2b2e3fd1634600ea6bdf" diff --git a/pyproject.toml b/pyproject.toml index dadf05bb8942..c59230d66cc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ pyarrow-hotfix = ">=0.4,<1" python-dateutil = ">=2.8.2,<3" pytz = ">=2022.7" rich = ">=12.4.4,<14" -sqlglot = ">=22.5,<23.1" +sqlglot = ">=23.4,<23.10" toolz = ">=0.11,<1" typing-extensions = ">=4.3.0,<5" black = { version = ">=22.1.0,<25", optional = true } diff --git a/requirements-dev.txt b/requirements-dev.txt index e8de1dc3d75a..86ae64dc403f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -236,7 +236,7 @@ sortedcontainers==2.4.0 ; python_version >= "3.9" and python_version < "4.0" soupsieve==2.5 ; python_version >= "3.10" and python_version < "3.13" sphobjinv==2.3.1 ; python_version >= "3.10" and python_version < "3.13" sqlalchemy==2.0.29 ; python_version >= "3.9" and python_version < "4.0" -sqlglot==23.0.5 ; python_version >= "3.9" and python_version < "4.0" +sqlglot==23.9.0 ; python_version >= "3.9" and python_version < "4.0" stack-data==0.6.3 ; python_version >= "3.9" and python_version < "4.0" statsmodels==0.14.1 ; python_version >= "3.10" and python_version < "3.13" stdlib-list==0.10.0 ; python_version >= "3.9" and python_version < "4.0"