Skip to content

Commit

Permalink
[WIP] Improving error handling messages for Custom Functions (#1330)
Browse files Browse the repository at this point in the history
Added separate error handling for ModuleNotFoundError and
FileNotFoundError
	modified:   evadb/utils/generic_utils.py
  • Loading branch information
hershd23 authored Nov 6, 2023
1 parent bd344ed commit 35b772b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 6 deletions.
17 changes: 14 additions & 3 deletions evadb/utils/generic_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,26 @@ def load_function_class_from_file(filepath, classname=None):
The class instance.
Raises:
RuntimeError: If the class name is not found or there is more than one class in the file.
ImportError: If the module cannot be loaded.
FileNotFoundError: If the file cannot be found.
RuntimeError: Any othe type of runtime error.
"""
try:
abs_path = Path(filepath).resolve()
spec = importlib.util.spec_from_file_location(abs_path.stem, abs_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
except ImportError as e:
# ImportError in the case when we are able to find the file but not able to load the module
err_msg = f"ImportError : Couldn't load function from {filepath} : {str(e)}. Not able to load the code provided in the file {abs_path}. Please ensure that the file contains the implementation code for the function."
raise ImportError(err_msg)
except FileNotFoundError as e:
# FileNotFoundError in the case when we are not able to find the file at all at the path.
err_msg = f"FileNotFoundError : Couldn't load function from {filepath} : {str(e)}. This might be because the function implementation file does not exist. Please ensure the file exists at {abs_path}"
raise FileNotFoundError(err_msg)
except Exception as e:
err_msg = f"Couldn't load function from {filepath} : {str(e)}. This might be due to a missing Python package, or because the function implementation file does not exist, or it is not a valid Python file."
# Default exception, we don't know what exactly went wrong so we just output the error message
err_msg = f"Couldn't load function from {filepath} : {str(e)}."
raise RuntimeError(err_msg)

# Try to load the specified class by name
Expand All @@ -97,7 +108,7 @@ def load_function_class_from_file(filepath, classname=None):
if obj.__module__ == module.__name__
]
if len(classes) != 1:
raise RuntimeError(
raise ImportError(
f"{filepath} contains {len(classes)} classes, please specify the correct class to load by naming the function with the same name in the CREATE query."
)
return classes[0]
Expand Down
17 changes: 14 additions & 3 deletions test/integration_tests/short/test_generic_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# limitations under the License.

import unittest
from pathlib import Path
from test.markers import windows_skip_marker

from evadb.configuration.constants import EvaDB_DATASET_DIR
Expand Down Expand Up @@ -50,17 +51,27 @@ def test_should_return_correct_class_for_path_without_classname(self):
assert vl.__qualname__ == DecordReader.__qualname__

def test_should_raise_on_missing_file(self):
with self.assertRaises(RuntimeError):
# Asserting on the error message, but that's brittle
with self.assertRaises(FileNotFoundError):
load_function_class_from_file("evadb/readers/opencv_reader_abdfdsfds.py")

def test_should_raise_on_empty_file(self):
# Asserting on the error message, but that's brittle
Path("/tmp/empty_file.py").touch()
with self.assertRaises(ImportError):
load_function_class_from_file("/tmp/empty_file.py")

# Cleanup
Path("/tmp/empty_file.py").unlink()

def test_should_raise_if_class_does_not_exists(self):
with self.assertRaises(RuntimeError):
with self.assertRaises(ImportError):
# evadb/utils/s3_utils.py has no class in it
# if this test fails due to change in s3_utils.py, change the file to something else
load_function_class_from_file("evadb/utils/s3_utils.py")

def test_should_raise_if_multiple_classes_exist_and_no_class_mentioned(self):
with self.assertRaises(RuntimeError):
with self.assertRaises(ImportError):
# evadb/utils/generic_utils.py has multiple classes in it
# if this test fails due to change in generic_utils.py, change the file to something else
load_function_class_from_file("evadb/utils/generic_utils.py")
Expand Down

0 comments on commit 35b772b

Please sign in to comment.