Repository: TobikoData/sqlmesh (python)

Issue:
`Unable to create a loaded context` after bumping to 0.118.5
Hello there,

I am getting this error after upgrading to `0.118.5`. 
The UI more specifically states `unable to created a loaded context`. 


This is the full traceback:
```
Traceback (most recent call last):
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/bin/sqlmesh", line 8, in <module>
    sys.exit(cli())
             ^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/click/core.py", line 1685, in invoke
    super().invoke(ctx)
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/click/decorators.py", line 33, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/cli/__init__.py", line 31, in wrapper
    return handler(sqlmesh_context, lambda: func(*args, **kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/cli/__init__.py", line 40, in _default_exception_handler
    return func()
           ^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/cli/__init__.py", line 31, in <lambda>
    return handler(sqlmesh_context, lambda: func(*args, **kwargs))
                                            ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/cli/main.py", line 96, in cli
    context = Context(
              ^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/context.py", line 379, in __init__
    self.load()
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/context.py", line 511, in load
    project = self._loader.load(self, update_schemas)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/loader.py", line 128, in load
    models = self._load_models(
             ^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/loader.py", line 336, in _load_models
    models.update(self._load_python_models(macros, jinja_macros))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/loader.py", line 423, in _load_python_models
    model = registry[name].model(
            ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/model/decorator.py", line 141, in model
    return create_python_model(
           ^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/model/definition.py", line 1931, in create_python_model
    _parse_dependencies(python_env, entrypoint) if python_env is not None else (set(), set())
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/core/model/definition.py", line 2228, in _parse_dependencies
    env = prepare_env(python_env)
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sotirisgkoulimaris/Documents/projects/dbol-elt/.venv/lib/python3.12/site-packages/sqlmesh/utils/metaprogramming.py", line 424, in prepare_env
    exec(executable.payload, env)
  File "<string>", line 1, in <module>
NameError: name 'func' is not defined
```

Pull request description:
Fix: handle metaprogramming inspect.getfile TypeError
Fixes #3054

cc @z3z1ma

Patch:
diff --git a/sqlmesh/utils/metaprogramming.py b/sqlmesh/utils/metaprogramming.py
index 31743c7a..2da34688 100644
--- a/sqlmesh/utils/metaprogramming.py
+++ b/sqlmesh/utils/metaprogramming.py
@@ -359,11 +359,12 @@ def serialize_env(env: t.Dict[str, t.Any], path: Path) -> t.Dict[str, Executable
             name = v.__name__
             name = k if name == "<lambda>" else name
 
-            # We can't call getfile on built-in callables
+            # getfile raises a `TypeError` for built-in modules, classes, or functions
             # https://docs.python.org/3/library/inspect.html#inspect.getfile
-            file_path = (
-                Path(inspect.getfile(inspect.unwrap(v))) if not inspect.isbuiltin(v) else None
-            )
+            try:
+                file_path = Path(inspect.getfile(v))
+            except TypeError:
+                file_path = None
 
             if _is_relative_to(file_path, path):
                 serialized[k] = Executable(


Test patch:
diff --git a/tests/utils/test_metaprogramming.py b/tests/utils/test_metaprogramming.py
index 8537851a..be360f9e 100644
--- a/tests/utils/test_metaprogramming.py
+++ b/tests/utils/test_metaprogramming.py
@@ -1,4 +1,5 @@
 import typing as t
+from contextlib import contextmanager
 from dataclasses import dataclass
 from pathlib import Path
 
@@ -45,7 +46,7 @@ def test_print_exception(mocker: MockerFixture):
 
     expected_message = f"""Traceback (most recent call last):
 
-  File "{__file__}", line 42, in test_print_exception
+  File "{__file__}", line 43, in test_print_exception
     eval("test_fun()", env)
 
   File "<string>", line 1, in <module>
@@ -106,6 +107,11 @@ def noop_metadata() -> None:
 setattr(noop_metadata, c.SQLMESH_METADATA, True)
 
 
+@contextmanager
+def test_context_manager():
+    yield
+
+
 def main_func(y: int, foo=exp.true(), *, bar=expressions.Literal.number(1) + 2) -> int:
     """DOC STRING"""
     sqlglot.parse_one("1")
@@ -117,6 +123,9 @@ def main_func(y: int, foo=exp.true(), *, bar=expressions.Literal.number(1) + 2)
     def closure(z: int) -> int:
         return z + Z
 
+    with test_context_manager():
+        pass
+
     return closure(y) + other_func(Y)
 
 
@@ -132,6 +141,7 @@ def test_func_globals() -> None:
         "sqlglot": sqlglot,
         "exp": exp,
         "expressions": exp,
+        "test_context_manager": test_context_manager,
     }
     assert func_globals(other_func) == {
         "X": 1,
@@ -167,6 +177,8 @@ def test_normalize_source() -> None:
 
     def closure(z: int):
         return z + Z
+    with test_context_manager():
+        pass
     return closure(y) + other_func(Y)"""
     )
 
@@ -194,6 +206,7 @@ def test_serialize_env() -> None:
     build_env(main_func, env=env, name="MAIN", path=path)
     env = serialize_env(env, path=path)  # type: ignore
 
+    assert prepare_env(env)
     assert env == {
         "MAIN": Executable(
             name="main_func",
@@ -209,11 +222,19 @@ def test_serialize_env() -> None:
 
     def closure(z: int):
         return z + Z
+    with test_context_manager():
+        pass
     return closure(y) + other_func(Y)""",
         ),
         "X": Executable(payload="1", kind=ExecutableKind.VALUE),
         "Y": Executable(payload="2", kind=ExecutableKind.VALUE),
         "Z": Executable(payload="3", kind=ExecutableKind.VALUE),
+        "_GeneratorContextManager": Executable(
+            payload="from contextlib import _GeneratorContextManager", kind=ExecutableKind.IMPORT
+        ),
+        "contextmanager": Executable(
+            payload="from contextlib import contextmanager", kind=ExecutableKind.IMPORT
+        ),
         "KLASS_X": Executable(payload="1", kind=ExecutableKind.VALUE),
         "KLASS_Y": Executable(payload="2", kind=ExecutableKind.VALUE),
         "KLASS_Z": Executable(payload="3", kind=ExecutableKind.VALUE),
@@ -255,6 +276,14 @@ class DataClass:
         "expressions": Executable(
             kind=ExecutableKind.IMPORT, payload="import sqlglot.expressions as expressions"
         ),
+        "func": Executable(
+            payload="""@contextmanager
+def test_context_manager():
+    yield""",
+            name="test_context_manager",
+            path="test_metaprogramming.py",
+            alias="func",
+        ),
         "my_lambda": Executable(
             name="my_lambda",
             path="test_metaprogramming.py",
@@ -282,4 +311,9 @@ class DataClass:
     my_lambda()
     return X + a""",
         ),
+        "test_context_manager": Executable(
+            payload="from tests.utils.test_metaprogramming import test_context_manager",
+            kind=ExecutableKind.IMPORT,
+        ),
+        "wraps": Executable(payload="from functools import wraps", kind=ExecutableKind.IMPORT),
     }