2424
2525if t .TYPE_CHECKING :
2626 from sqlglot .dialects .dialect import DialectType
27+ from sqlmesh .utils import registry_decorator
2728 from sqlmesh .utils .jinja import MacroReference
2829
30+ MacroCallable = registry_decorator
31+
2932
3033def make_python_env (
3134 expressions : t .Union [exp .Expression , t .List [exp .Expression ]],
@@ -43,56 +46,74 @@ def make_python_env(
4346 python_env = {} if python_env is None else python_env
4447 variables = variables or {}
4548 env : t .Dict [str , t .Tuple [t .Any , t .Optional [bool ]]] = {}
46- used_macros = {}
49+ used_macros : t .Dict [
50+ str ,
51+ t .Tuple [t .Union [Executable | MacroCallable ], t .Optional [bool ]],
52+ ] = {}
4753 used_variables = (used_variables or set ()).copy ()
4854
4955 expressions = ensure_list (expressions )
5056 for expression in expressions :
51- if not isinstance (expression , d .Jinja ):
52- for macro_func_or_var in expression .find_all (d .MacroFunc , d .MacroVar , exp .Identifier ):
53- if macro_func_or_var .__class__ is d .MacroFunc :
54- name = macro_func_or_var .this .name .lower ()
55- if name in macros :
56- used_macro = macros [name ]
57- if callable (used_macro ) and expression .meta .get ("metadata_only" ):
58- setattr (used_macro .func , c .SQLMESH_METADATA , True )
59-
60- used_macros [name ] = used_macro
61- if name == c .VAR :
62- args = macro_func_or_var .this .expressions
63- if len (args ) < 1 :
64- raise_config_error ("Macro VAR requires at least one argument" , path )
65- if not args [0 ].is_string :
66- raise_config_error (
67- f"The variable name must be a string literal, '{ args [0 ].sql ()} ' was given instead" ,
68- path ,
69- )
70- used_variables .add (args [0 ].this .lower ())
71- elif macro_func_or_var .__class__ is d .MacroVar :
72- name = macro_func_or_var .name .lower ()
73- if name in macros :
74- used_macros [name ] = macros [name ]
75- elif name in variables :
76- used_variables .add (name )
77- elif (
78- isinstance (macro_func_or_var , (exp .Identifier , d .MacroStrReplace , d .MacroSQL ))
79- ) and "@" in macro_func_or_var .name :
80- for _ , identifier , braced_identifier , _ in MacroStrTemplate .pattern .findall (
81- macro_func_or_var .name
82- ):
83- var_name = braced_identifier or identifier
84- if var_name in variables :
85- used_variables .add (var_name )
57+ if isinstance (expression , d .Jinja ):
58+ continue
59+
60+ for macro_func_or_var in expression .find_all (d .MacroFunc , d .MacroVar , exp .Identifier ):
61+ if macro_func_or_var .__class__ is d .MacroFunc :
62+ name = macro_func_or_var .this .name .lower ()
63+ if name not in macros :
64+ continue
65+
66+ # If this macro has been seen before as a non-metadata macro, prioritize that
67+ used_macros [name ] = (
68+ macros [name ],
69+ (used_macros .get (name ) or (None , expression .meta .get ("is_metadata" )))[1 ],
70+ )
71+ if name == c .VAR :
72+ args = macro_func_or_var .this .expressions
73+ if len (args ) < 1 :
74+ raise_config_error ("Macro VAR requires at least one argument" , path )
75+ if not args [0 ].is_string :
76+ raise_config_error (
77+ f"The variable name must be a string literal, '{ args [0 ].sql ()} ' was given instead" ,
78+ path ,
79+ )
80+ used_variables .add (args [0 ].this .lower ())
81+ elif macro_func_or_var .__class__ is d .MacroVar :
82+ name = macro_func_or_var .name .lower ()
83+ if name in macros :
84+ # If this macro has been seen before as a non-metadata macro, prioritize that
85+ used_macros [name ] = (
86+ macros [name ],
87+ (used_macros .get (name ) or (None , expression .meta .get ("is_metadata" )))[1 ],
88+ )
89+ elif name in variables :
90+ used_variables .add (name )
91+ elif (
92+ isinstance (macro_func_or_var , (exp .Identifier , d .MacroStrReplace , d .MacroSQL ))
93+ ) and "@" in macro_func_or_var .name :
94+ for _ , identifier , braced_identifier , _ in MacroStrTemplate .pattern .findall (
95+ macro_func_or_var .name
96+ ):
97+ var_name = braced_identifier or identifier
98+ if var_name in variables :
99+ used_variables .add (var_name )
86100
87101 for macro_ref in jinja_macro_references or set ():
88102 if macro_ref .package is None and macro_ref .name in macros :
89- used_macros [macro_ref .name ] = macros [macro_ref .name ]
103+ used_macros [macro_ref .name ] = ( macros [macro_ref .name ], None )
90104
91- for name , used_macro in used_macros .items ():
105+ for name , ( used_macro , is_metadata ) in used_macros .items ():
92106 if isinstance (used_macro , Executable ):
93107 python_env [name ] = used_macro
94108 elif not hasattr (used_macro , c .SQLMESH_BUILTIN ) and name not in python_env :
109+ used_macro_func = used_macro .func
110+ previous_is_metadata = getattr (used_macro_func , c .SQLMESH_METADATA , None )
111+
112+ if is_metadata :
113+ setattr (used_macro_func , c .SQLMESH_METADATA , is_metadata )
114+
95115 build_env (used_macro .func , env = env , name = name , path = module_path )
116+ setattr (used_macro_func , c .SQLMESH_METADATA , previous_is_metadata )
96117
97118 python_env .update (serialize_env (env , path = module_path ))
98119 return _add_variables_to_python_env (
0 commit comments