From 2fa6b7cd441b722e5cc40352eda5d4332a5b18ee Mon Sep 17 00:00:00 2001 From: lafirm <136463254+lafirm@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:24:36 +0530 Subject: [PATCH 1/5] chore: drop astor and use ast.unparse Signed-off-by: lafirm <136463254+lafirm@users.noreply.github.com> --- pyproject.toml | 2 -- sqlmesh/core/model/common.py | 3 +-- sqlmesh/utils/metaprogramming.py | 4 +--- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 22e9b9a18c..1533f15979 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,6 @@ authors = [{ name = "SQLMesh Contributors" }] license = { file = "LICENSE" } requires-python = ">= 3.9" dependencies = [ - "astor", "click", "croniter", "duckdb>=0.10.0,!=0.10.3", @@ -198,7 +197,6 @@ disable_error_code = "annotation-unchecked" [[tool.mypy.overrides]] module = [ "api.*", - "astor.*", "IPython.*", "hyperscript.*", "py.*", diff --git a/sqlmesh/core/model/common.py b/sqlmesh/core/model/common.py index c75531afb8..f03cf49753 100644 --- a/sqlmesh/core/model/common.py +++ b/sqlmesh/core/model/common.py @@ -4,7 +4,6 @@ import typing as t from pathlib import Path -from astor import to_source from difflib import get_close_matches from sqlglot import exp from sqlglot.helper import ensure_list @@ -387,7 +386,7 @@ def get_first_arg(keyword_arg_name: str) -> t.Any: ) try: - expression = to_source(first_arg) + expression = ast.unparse(t.cast(ast.expr, first_arg)) return eval(expression, env, local_env) except Exception: if strict_resolution: diff --git a/sqlmesh/utils/metaprogramming.py b/sqlmesh/utils/metaprogramming.py index cd77c36353..824c2d5b26 100644 --- a/sqlmesh/utils/metaprogramming.py +++ b/sqlmesh/utils/metaprogramming.py @@ -17,8 +17,6 @@ from numbers import Number from pathlib import Path -from astor import to_source - from sqlmesh.core import constants as c from sqlmesh.utils import format_exception, unique from sqlmesh.utils.errors import SQLMeshError @@ -274,7 +272,7 @@ def normalize_source(obj: t.Any) -> str: if isinstance(node, ast.FunctionDef): node.returns = None - return to_source(root_node).strip() + return ast.unparse(root_node).strip() def build_env( From 45674b967eb009db18e310db416ee8aa7fb6a517 Mon Sep 17 00:00:00 2001 From: lafirm <136463254+lafirm@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:27:02 +0530 Subject: [PATCH 2/5] chore: replace ast.Str for py3.14 Signed-off-by: lafirm <136463254+lafirm@users.noreply.github.com> --- sqlmesh/utils/metaprogramming.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sqlmesh/utils/metaprogramming.py b/sqlmesh/utils/metaprogramming.py index 824c2d5b26..a5bd376566 100644 --- a/sqlmesh/utils/metaprogramming.py +++ b/sqlmesh/utils/metaprogramming.py @@ -265,7 +265,12 @@ def normalize_source(obj: t.Any) -> str: # remove docstrings body = node.body - if body and isinstance(body[0], ast.Expr) and isinstance(body[0].value, ast.Str): + if ( + body + and isinstance(body[0], ast.Expr) + and isinstance(body[0].value, ast.Constant) + and isinstance(body[0].value.value, str) + ): node.body = body[1:] # remove function return type annotation From d34b19c090770ab1366239a65e3ff501b1a8e938 Mon Sep 17 00:00:00 2001 From: lafirm <136463254+lafirm@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:28:58 +0530 Subject: [PATCH 3/5] fix: min python version in doc Signed-off-by: lafirm <136463254+lafirm@users.noreply.github.com> --- docs/prerequisites.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/prerequisites.md b/docs/prerequisites.md index 11acfca64e..540a6b4885 100644 --- a/docs/prerequisites.md +++ b/docs/prerequisites.md @@ -4,7 +4,7 @@ This page describes the system prerequisites needed to run SQLMesh and provides ## SQLMesh prerequisites -You'll need Python 3.8 or higher to use SQLMesh. You can check your python version by running the following command: +You'll need Python 3.9 or higher to use SQLMesh. You can check your python version by running the following command: ```bash python3 --version ``` From 1266f1e4eb38110733d8a510aee2c343461aeb6d Mon Sep 17 00:00:00 2001 From: lafirm <136463254+lafirm@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:43:46 +0530 Subject: [PATCH 4/5] fix: update tests Signed-off-by: lafirm <136463254+lafirm@users.noreply.github.com> --- tests/utils/test_metaprogramming.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/utils/test_metaprogramming.py b/tests/utils/test_metaprogramming.py index 9a6f0c95cd..20146c8d02 100644 --- a/tests/utils/test_metaprogramming.py +++ b/tests/utils/test_metaprogramming.py @@ -220,8 +220,7 @@ def closure() -> int: def test_normalize_source() -> None: assert ( normalize_source(main_func) - == """def main_func(y: int, foo=exp.true(), *, bar=expressions.Literal.number(1) + 2 - ): + == """def main_func(y: int, foo=exp.true(), *, bar=expressions.Literal.number(1) + 2): sqlglot.parse_one('1') MyClass(47) DataClass(x=y) @@ -271,8 +270,7 @@ def test_serialize_env() -> None: name="main_func", alias="MAIN", path="test_metaprogramming.py", - payload="""def main_func(y: int, foo=exp.true(), *, bar=expressions.Literal.number(1) + 2 - ): + payload="""def main_func(y: int, foo=exp.true(), *, bar=expressions.Literal.number(1) + 2): sqlglot.parse_one('1') MyClass(47) DataClass(x=y) @@ -370,7 +368,7 @@ def sample_context_manager(): "my_lambda": Executable( name="my_lambda", path="test_metaprogramming.py", - payload="my_lambda = lambda : print('z')", + payload="my_lambda = lambda: print('z')", ), "normalize_model_name": Executable( payload="from sqlmesh.core.dialect import normalize_model_name", From 2fe6224e4938dccca080cfbd9a2cb6fd3f7c37ed Mon Sep 17 00:00:00 2001 From: lafirm <136463254+lafirm@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:15:37 +0530 Subject: [PATCH 5/5] fix(test): match normalize_source output across Python versions Signed-off-by: lafirm <136463254+lafirm@users.noreply.github.com> --- tests/utils/test_metaprogramming.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/utils/test_metaprogramming.py b/tests/utils/test_metaprogramming.py index 20146c8d02..1f1431b963 100644 --- a/tests/utils/test_metaprogramming.py +++ b/tests/utils/test_metaprogramming.py @@ -1,3 +1,4 @@ +import ast import typing as t from contextlib import contextmanager from dataclasses import dataclass @@ -50,7 +51,7 @@ def test_print_exception(mocker: MockerFixture): except Exception as ex: print_exception(ex, test_env, out_mock) - expected_message = r""" File ".*?.tests.utils.test_metaprogramming\.py", line 49, in test_print_exception + expected_message = r""" File ".*?.tests.utils.test_metaprogramming\.py", line 50, in test_print_exception eval\("test_fun\(\)", env\).* File '/test/path.py' \(or imported file\), line 2, in test_fun @@ -368,7 +369,8 @@ def sample_context_manager(): "my_lambda": Executable( name="my_lambda", path="test_metaprogramming.py", - payload="my_lambda = lambda: print('z')", + # Match normalize_source output across Python versions + payload=ast.unparse(ast.parse("my_lambda = lambda: print('z')")).strip(), ), "normalize_model_name": Executable( payload="from sqlmesh.core.dialect import normalize_model_name",