@@ -8696,10 +8696,12 @@ def metadata_macro(evaluator):
86968696 )
86978697
86988698 model = ctx .get_model ("test_model" )
8699+ python_env = model .python_env
8700+
86998701 empty_executable = Executable (payload = "" )
87008702
8701- assert len (model . python_env ) == 1
8702- assert (model . python_env .get ("metadata_macro" ) or empty_executable ).is_metadata
8703+ assert len (python_env ) == 1
8704+ assert (python_env .get ("metadata_macro" ) or empty_executable ).is_metadata
87038705
87048706 ctx .plan (no_prompts = True , auto_apply = True )
87058707
@@ -8708,10 +8710,11 @@ def metadata_macro(evaluator):
87088710
87098711 ctx .load ()
87108712 model = ctx .get_model ("test_model" )
8713+ python_env = model .python_env
87118714
8712- assert len (model . python_env ) == 2
8713- assert (model . python_env .get ("metadata_macro" ) or empty_executable ).is_metadata
8714- assert (model . python_env .get ("parse_one" ) or empty_executable ).is_metadata
8715+ assert len (python_env ) == 2
8716+ assert (python_env .get ("metadata_macro" ) or empty_executable ).is_metadata
8717+ assert (python_env .get ("parse_one" ) or empty_executable ).is_metadata
87158718
87168719 plan = ctx .plan (no_prompts = True , auto_apply = True )
87178720 ctx_diff = plan .context_diff
@@ -8727,11 +8730,12 @@ def metadata_macro(evaluator):
87278730
87288731 ctx .load ()
87298732 model = ctx .get_model ("test_model" )
8733+ python_env = model .python_env
87308734
8731- assert len (model . python_env ) == 3
8732- assert (model . python_env .get ("metadata_macro" ) or empty_executable ).is_metadata
8733- assert (model . python_env .get ("get_parsed_query" ) or empty_executable ).is_metadata
8734- assert (model . python_env .get ("parse_one" ) or empty_executable ).is_metadata
8735+ assert len (python_env ) == 3
8736+ assert (python_env .get ("metadata_macro" ) or empty_executable ).is_metadata
8737+ assert (python_env .get ("get_parsed_query" ) or empty_executable ).is_metadata
8738+ assert (python_env .get ("parse_one" ) or empty_executable ).is_metadata
87358739
87368740 plan = ctx .plan (no_prompts = True , auto_apply = True )
87378741 ctx_diff = plan .context_diff
@@ -8783,15 +8787,95 @@ def m2(evaluator):
87838787 model = ctx .get_model ("test_model" )
87848788 empty_executable = Executable (payload = "" )
87858789
8790+ python_env = model .python_env
8791+
87868792 # Both `m1` and `m2` refer to `parse_one`, so for `m1` it would be a transitive metadata-only
87878793 # object, but since the python env is a flat namespace and `parse_one` is also a dependency
87888794 # of `m2`, it needs to be treated as non-metadata.
8789- assert len (model .python_env ) == 5
8790- assert (model .python_env .get ("m1" ) or empty_executable ).is_metadata
8791- assert (model .python_env .get ("m1_dep" ) or empty_executable ).is_metadata
8792- assert not (model .python_env .get ("m2" ) or empty_executable ).is_metadata
8793- assert not (model .python_env .get ("parse_one" ) or empty_executable ).is_metadata
8794- assert not (model .python_env .get ("common_dep" ) or empty_executable ).is_metadata
8795+ assert len (python_env ) == 5
8796+ assert (python_env .get ("m1" ) or empty_executable ).is_metadata
8797+ assert (python_env .get ("m1_dep" ) or empty_executable ).is_metadata
8798+ assert not (python_env .get ("m2" ) or empty_executable ).is_metadata
8799+ assert not (python_env .get ("parse_one" ) or empty_executable ).is_metadata
8800+ assert not (python_env .get ("common_dep" ) or empty_executable ).is_metadata
8801+
8802+
8803+ def test_macros_referenced_in_audits_or_signals_are_metadata_only (tmp_path : Path ) -> None :
8804+ init_example_project (tmp_path , dialect = "duckdb" , template = ProjectTemplate .EMPTY )
8805+
8806+ test_model = tmp_path / "models/test_model.sql"
8807+ test_model .parent .mkdir (parents = True , exist_ok = True )
8808+ test_model .write_text (
8809+ """
8810+ MODEL (
8811+ name test_model,
8812+ kind FULL,
8813+ signals (
8814+ test_signal_always_true(arg := @m1())
8815+ ),
8816+ audits (
8817+ unique_values(columns := @m2())
8818+ ),
8819+ );
8820+
8821+ SELECT
8822+ 1 AS c
8823+ """
8824+ )
8825+
8826+ macro_code = """
8827+ from sqlglot import exp
8828+ from sqlmesh import macro
8829+
8830+ def baz():
8831+ pass
8832+
8833+ @macro()
8834+ def m1(evaluator):
8835+ baz()
8836+ return 1
8837+
8838+ @macro()
8839+ def m2(evaluator):
8840+ return exp.column("c")"""
8841+
8842+ test_macros = tmp_path / "macros/test_macros.py"
8843+ test_macros .parent .mkdir (parents = True , exist_ok = True )
8844+ test_macros .write_text (macro_code )
8845+
8846+ signal_code = """
8847+ import typing as t
8848+
8849+ from sqlmesh import signal
8850+
8851+ def bar():
8852+ pass
8853+
8854+ @signal()
8855+ def test_signal_always_true(batch, arg):
8856+ bar()
8857+ return True"""
8858+
8859+ test_signals = tmp_path / "signals/test_signals.py"
8860+ test_signals .parent .mkdir (parents = True , exist_ok = True )
8861+ test_signals .write_text (signal_code )
8862+
8863+ ctx = Context (
8864+ config = Config (model_defaults = ModelDefaultsConfig (dialect = "duckdb" )),
8865+ paths = tmp_path ,
8866+ )
8867+ model = ctx .get_model ("test_model" )
8868+ empty_executable = Executable (payload = "" )
8869+
8870+ python_env = model .python_env
8871+
8872+ assert len (python_env ) == 6
8873+ assert (python_env .get ("test_signal_always_true" ) or empty_executable ).is_metadata
8874+ assert (python_env .get ("bar" ) or empty_executable ).is_metadata
8875+ assert (python_env .get ("m1" ) or empty_executable ).is_metadata
8876+ assert (python_env .get ("baz" ) or empty_executable ).is_metadata
8877+ assert (python_env .get ("m2" ) or empty_executable ).is_metadata
8878+ assert (python_env .get ("exp" ) or empty_executable ).is_metadata
87958879
87968880
87978881def test_scd_type_2_full_history_restatement ():
0 commit comments