Repository: great-expectations/great_expectations

Issue:
Use cleaner solution for non-truncating division in python 2
Prefer `from __future__ import division` to `1.*x/y`


Discussion:
Done inline during improve_partitions push.

Resolving patch:
diff --git a/great_expectations/validator/exception_info.py b/great_expectations/validator/exception_info.py
new file mode 100644
--- /dev/null
+++ b/great_expectations/validator/exception_info.py
@@ -0,0 +1,73 @@
+import json
+from typing import Optional
+
+from great_expectations.core import IDDict
+from great_expectations.core.util import convert_to_json_serializable
+from great_expectations.types.base import SerializableDotDict
+
+
+class ExceptionInfo(SerializableDotDict):
+    def __init__(
+        self,
+        exception_traceback: str,
+        exception_message: str,
+        raised_exception: Optional[bool] = True,
+    ):
+        super().__init__(
+            exception_traceback=exception_traceback,
+            exception_message=exception_message,
+            raised_exception=raised_exception,
+        )
+
+    def to_json_dict(self) -> dict:
+        fields_dict: dict = {
+            "exception_traceback": self.exception_traceback,
+            "exception_message": self.exception_message,
+            "raised_exception": self.raised_exception,
+        }
+        return convert_to_json_serializable(fields_dict)
+
+    @property
+    def exception_traceback(self) -> str:
+        return self["exception_traceback"]
+
+    @property
+    def exception_message(self) -> str:
+        return self["exception_message"]
+
+    @property
+    def raised_exception(self) -> bool:
+        return self["raised_exception"]
+
+    def __repr__(self) -> str:
+        fields_dict: dict = {
+            "exception_traceback": self.exception_traceback,
+            "exception_message": self.exception_message,
+            "raised_exception": self.raised_exception,
+        }
+        return str(fields_dict)
+
+    @property
+    def id(self) -> str:
+        return IDDict(self.to_json_dict()).to_id()
+
+    def __eq__(self, other):
+        if not isinstance(other, self.__class__):
+            # Delegate comparison to the other instance's __eq__.
+            return NotImplemented
+        return self.id == other.id
+
+    def __ne__(self, other):
+        return not self.__eq__(other=other)
+
+    def __str__(self):
+        return json.dumps(self.to_json_dict(), indent=2)
+
+    def __hash__(self) -> int:
+        """Overrides the default implementation"""
+        _result_hash: int = (
+            hash(self.exception_traceback)
+            ^ hash(self.exception_message)
+            ^ hash(self.raised_exception)
+        )
+        return _result_hash
diff --git a/great_expectations/validator/validator.py b/great_expectations/validator/validator.py
--- a/great_expectations/validator/validator.py
+++ b/great_expectations/validator/validator.py
@@ -48,6 +48,7 @@
 from great_expectations.marshmallow__shade import ValidationError
 from great_expectations.types import ClassConfig
 from great_expectations.util import load_class, verify_dynamic_loading_support
+from great_expectations.validator.exception_info import ExceptionInfo
 from great_expectations.validator.metric_configuration import MetricConfiguration
 from great_expectations.validator.validation_graph import MetricEdge, ValidationGraph
 
@@ -264,21 +265,19 @@ def inst_expectation(*args, **kwargs):
 
             except Exception as err:
                 if basic_runtime_configuration.get("catch_exceptions"):
-                    raised_exception = True
                     exception_traceback = traceback.format_exc()
                     exception_message = f"{type(err).__name__}: {str(err)}"
-
+                    exception_info = ExceptionInfo(
+                        **{
+                            "exception_traceback": exception_traceback,
+                            "exception_message": exception_message,
+                        }
+                    )
                     validation_result = ExpectationValidationResult(
-                        expectation_config=configuration,
                         success=False,
+                        exception_info=exception_info,
+                        expectation_config=configuration,
                     )
-
-                    validation_result.exception_info = {
-                        "raised_exception": raised_exception,
-                        "exception_message": exception_message,
-                        "exception_traceback": exception_traceback,
-                    }
-
                 else:
                     raise err
             return validation_result
@@ -451,16 +450,18 @@ def graph_validate(
                 processed_configurations.append(evaluated_config)
             except Exception as err:
                 if catch_exceptions:
-                    raised_exception = True
                     exception_traceback = traceback.format_exc()
+                    exception_message = str(err)
+                    exception_info = ExceptionInfo(
+                        **{
+                            "exception_traceback": exception_traceback,
+                            "exception_message": exception_message,
+                        }
+                    )
                     result = ExpectationValidationResult(
                         success=False,
-                        exception_info={
-                            "raised_exception": raised_exception,
-                            "exception_traceback": exception_traceback,
-                            "exception_message": str(err),
-                        },
-                        expectation_config=evaluated_config,
+                        exception_info=exception_info,
+                        expectation_config=configuration,
                     )
                     evrs.append(result)
                 else:
@@ -479,16 +480,18 @@ def graph_validate(
             )
         except Exception as err:
             if catch_exceptions:
-                raised_exception = True
                 exception_traceback = traceback.format_exc()
+                exception_message = str(err)
+                exception_info = ExceptionInfo(
+                    **{
+                        "exception_traceback": exception_traceback,
+                        "exception_message": exception_message,
+                    }
+                )
                 for configuration in processed_configurations:
                     result = ExpectationValidationResult(
                         success=False,
-                        exception_info={
-                            "raised_exception": raised_exception,
-                            "exception_traceback": exception_traceback,
-                            "exception_message": str(err),
-                        },
+                        exception_info=exception_info,
                         expectation_config=configuration,
                     )
                     evrs.append(result)
@@ -506,16 +509,17 @@ def graph_validate(
                 evrs.append(result)
             except Exception as err:
                 if catch_exceptions:
-                    raised_exception = True
                     exception_traceback = traceback.format_exc()
-
+                    exception_message = str(err)
+                    exception_info = ExceptionInfo(
+                        **{
+                            "exception_traceback": exception_traceback,
+                            "exception_message": exception_message,
+                        }
+                    )
                     result = ExpectationValidationResult(
                         success=False,
-                        exception_info={
-                            "raised_exception": raised_exception,
-                            "exception_traceback": exception_traceback,
-                            "exception_message": str(err),
-                        },
+                        exception_info=exception_info,
                         expectation_config=configuration,
                     )
                     evrs.append(result)


Test patch: