Skip to content

Commit 14ba624

Browse files
authored
Fix: Improve inference of upstream dependencies for python models (#2377)
1 parent 440eb55 commit 14ba624

File tree

3 files changed

+33
-35
lines changed

3 files changed

+33
-35
lines changed

examples/sushi/models/order_items.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
ITEMS = "sushi.items"
1616

1717

18+
def get_items_table(context: ExecutionContext) -> str:
19+
return context.table(ITEMS)
20+
21+
1822
@model(
1923
"sushi.order_items",
2024
kind=IncrementalByTimeRangeKind(
@@ -45,7 +49,7 @@ def execute(
4549
**kwargs: t.Any,
4650
) -> t.Generator[pd.DataFrame, None, None]:
4751
orders_table = context.table("sushi.orders")
48-
items_table = context.table(ITEMS)
52+
items_table = get_items_table(context)
4953

5054
rng = random.Random()
5155

sqlmesh/core/model/definition.py

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,7 +1671,7 @@ def create_python_model(
16711671
# Find dependencies for python models by parsing code if they are not explicitly defined
16721672
# Also remove self-references that are found
16731673
depends_on = (
1674-
_parse_depends_on(entrypoint, python_env) - {name}
1674+
_parse_depends_on(python_env) - {name}
16751675
if depends_on is None and python_env is not None
16761676
else depends_on
16771677
)
@@ -1848,42 +1848,41 @@ def _python_env(
18481848
return serialized_env
18491849

18501850

1851-
def _parse_depends_on(
1852-
model_func: str,
1853-
python_env: t.Dict[str, Executable],
1854-
) -> t.Set[str]:
1851+
def _parse_depends_on(python_env: t.Dict[str, Executable]) -> t.Set[str]:
18551852
"""Parses the source of a model function and finds upstream dependencies based on calls to context."""
18561853
env = prepare_env(python_env)
18571854
depends_on = set()
1858-
executable = python_env[model_func]
18591855

1860-
for node in ast.walk(ast.parse(executable.payload)):
1861-
if not isinstance(node, ast.Call):
1856+
for executable in python_env.values():
1857+
if not executable.is_definition:
18621858
continue
1859+
for node in ast.walk(ast.parse(executable.payload)):
1860+
if not isinstance(node, ast.Call):
1861+
continue
18631862

1864-
func = node.func
1863+
func = node.func
18651864

1866-
if (
1867-
isinstance(func, ast.Attribute)
1868-
and isinstance(func.value, ast.Name)
1869-
and func.value.id == "context"
1870-
and func.attr == "table"
1871-
):
1872-
if node.args:
1873-
table: t.Optional[ast.expr] = node.args[0]
1874-
else:
1875-
table = next(
1876-
(keyword.value for keyword in node.keywords if keyword.arg == "model_name"),
1877-
None,
1878-
)
1865+
if (
1866+
isinstance(func, ast.Attribute)
1867+
and isinstance(func.value, ast.Name)
1868+
and func.value.id == "context"
1869+
and func.attr == "table"
1870+
):
1871+
if node.args:
1872+
table: t.Optional[ast.expr] = node.args[0]
1873+
else:
1874+
table = next(
1875+
(keyword.value for keyword in node.keywords if keyword.arg == "model_name"),
1876+
None,
1877+
)
18791878

1880-
try:
1881-
expression = to_source(table)
1882-
depends_on.add(eval(expression, env))
1883-
except Exception:
1884-
raise ConfigError(
1885-
f"Error resolving dependencies for '{executable.path}'. References to context must be resolvable at parse time.\n\n{expression}"
1886-
)
1879+
try:
1880+
expression = to_source(table)
1881+
depends_on.add(eval(expression, env))
1882+
except Exception:
1883+
raise ConfigError(
1884+
f"Error resolving dependencies for '{executable.path}'. References to context must be resolvable at parse time.\n\n{expression}"
1885+
)
18871886

18881887
return depends_on
18891888

sqlmesh/utils/metaprogramming.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ class ExecutableKind(str, Enum):
294294
IMPORT = "import"
295295
VALUE = "value"
296296
DEFINITION = "definition"
297-
STATEMENT = "statement"
298297

299298
def __lt__(self, other: t.Any) -> bool:
300299
if not isinstance(other, ExecutableKind):
@@ -321,10 +320,6 @@ def is_definition(self) -> bool:
321320
def is_import(self) -> bool:
322321
return self.kind == ExecutableKind.IMPORT
323322

324-
@property
325-
def is_statement(self) -> bool:
326-
return self.kind == ExecutableKind.STATEMENT
327-
328323
@property
329324
def is_value(self) -> bool:
330325
return self.kind == ExecutableKind.VALUE

0 commit comments

Comments
 (0)