Repository: fal-ai/dbt-fal

Issue:
Python script should be able to handle relative imports
I was trying execute a script using `fal`, it works fine when full code is in a single script but breaks down when I write down my script to different modules. Probably this is because fal is internally using python's `exec` builtins function to execute the script after reading the file. Would appreciate it very much if you guys can add this feature to fal as soon as possible. It is a great tool to work with dbt.! :D


Discussion:
Hi @RakibulHoque! Thanks for raising this issue. I can confirm that splitting code into modules doesn't yet work with fal. Will start working on it ASAP.
Thanks for quick response. :D

Resolving patch:
diff --git a/src/fal/cli/fal_runner.py b/src/fal/cli/fal_runner.py
--- a/src/fal/cli/fal_runner.py
+++ b/src/fal/cli/fal_runner.py
@@ -1,5 +1,6 @@
 import argparse
 from typing import List
+from pathlib import Path
 import os
 
 import dbt.exceptions
@@ -105,7 +106,7 @@ def _select_scripts(args_dict, models, project, args) -> List[FalScript]:
             model_scripts = model.get_scripts(args.keyword, args_dict.get("before"))
             for el in args.scripts:
                 if el in model_scripts:
-                    scripts.append(FalScript(model, el))
+                    scripts.append(FalScript(model, Path(el)))
     else:
         real_project_dir = os.path.realpath(os.path.normpath(args.project_dir))
         for model in models:
diff --git a/src/fal/fal_script.py b/src/fal/fal_script.py
--- a/src/fal/fal_script.py
+++ b/src/fal/fal_script.py
@@ -2,6 +2,7 @@
 from typing import List, TypeVar, Dict, Union
 from faldbt.project import DbtModel, FalDbt
 from pathlib import Path
+import sys
 
 T = TypeVar("T", bound="FalScript")
 
@@ -19,6 +20,11 @@ def exec(self, context, faldbt: FalDbt):
         """
         Executes the script
         """
+
+        # Enable local imports
+        local_path = str(self.path.parent)
+        sys.path.append(local_path)
+
         with open(self.path) as file:
             a_script = file.read()
             exec(
@@ -35,3 +41,4 @@ def exec(self, context, faldbt: FalDbt):
                     "list_features": faldbt.list_features,
                 },
             )
+            sys.path.remove(local_path)


Test patch:
diff --git a/integration_tests/mock/fal_scripts/my_util.py b/integration_tests/mock/fal_scripts/my_util.py
new file mode 100644
--- /dev/null
+++ b/integration_tests/mock/fal_scripts/my_util.py
@@ -0,0 +1,6 @@
+def handle_dbt_test(tests, output, model_name):
+    for test in tests:
+        output += f"\nRan {test.name} for {test.column}, result: {test.status}"
+    f = open(f"temp/{model_name}_test", "w")
+    f.write(output)
+    f.close()
diff --git a/integration_tests/mock/fal_scripts/test.py b/integration_tests/mock/fal_scripts/test.py
--- a/integration_tests/mock/fal_scripts/test.py
+++ b/integration_tests/mock/fal_scripts/test.py
@@ -1,16 +1,13 @@
 import pandas as pd
 import io
+import my_util
 
 model_name = context.current_model.name
 
 output = f"Model name: {model_name}"
 
 if context.current_model.status == "tested":
-    for test in context.current_model.tests:
-        output += f"\nRan {test.name} for {test.column}, result: {test.status}"
-    f = open(f"temp/{model_name}_test", "w")
-    f.write(output)
-    f.close()
+    my_util.handle_dbt_test(context.current_model.tests, output, model_name)
 
 else:
     output = f"Model name: {model_name}"