Skip to content

Commit

Permalink
Make sure ExaError does not raise (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicoretti authored Oct 23, 2024
1 parent 7a3c35b commit 0b113dc
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 106 deletions.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ you can use the generate subcommand.
ec generate NAME VERSION PACKAGE_ROOT > error-codes.json
```

## Known Issues
* [Throws exception on invalid error code format](https://github.com/exasol/error-reporting-python/issues/27)

### Information for Users

* [User Guide](doc/user_guide/user_guide.md)
Expand Down
9 changes: 7 additions & 2 deletions doc/changes/unreleased.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Unreleased


## Internal
* Relock Dependencies
## Fixes

* [#27](https://github.com/exasol/error-reporting-python/issues/27) Make sure creating an error does not raise an exception.

## Feature

Expand All @@ -11,3 +12,7 @@
## Refactorings

* #33: Updated to Python 3.10

## Internal
* Relock Dependencies

36 changes: 36 additions & 0 deletions error-codes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://schemas.exasol.com/error_code_report-1.0.0.json",
"projectName": "exasol-error-reporting",
"projectVersion": "0.4.0",
"errorCodes": [
{
"identifier": "E-ERP-1",
"message": "Invalid error code {{code}}.",
"messagePlaceholders": [
{
"placeholder": "code",
"description": "Error code which was causing the error."
}
],
"mitigations": [
"Ensure you follow the standard error code format."
],
"sourceFile": "_error.py"
},
{
"identifier": "E-ERP-2",
"message": "Unknown error/exception occurred.",
"messagePlaceholders": [
{
"placeholder": "traceback",
"description": "Exception traceback which lead to the generation of this error."
}
],
"description": "An unexpected error occurred during the creation of the error",
"mitigations": [
"A good starting point would be to investigate the cause of the attached exception.\n\nTrackback:\n {{traceback}}"
],
"sourceFile": "_error.py"
}
]
}
127 changes: 89 additions & 38 deletions exasol/error/_error.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import warnings
from dataclasses import dataclass
from inspect import cleandoc
from typing import Dict, Iterable, List, Mapping, Union
from pathlib import Path

with warnings.catch_warnings():
warnings.simplefilter("ignore")
from exasol_error_reporting_python import exa_error
from exasol_error_reporting_python.error_message_builder import InvalidErrorCode


@dataclass(frozen=True)
Expand All @@ -15,11 +18,11 @@ class Parameter:

class Error:
def __init__(
self,
code: str,
message: str,
mitigations: Union[str, Iterable[str]],
parameters: Dict[str, Union[str, Parameter]],
self,
code: str,
message: str,
mitigations: Union[str, Iterable[str]],
parameters: Dict[str, Union[str, Parameter]],
):
# This function maybe flattened into or moved out of the constructor in the future.
def build_error(code, msg, mitigations, params):
Expand Down Expand Up @@ -47,11 +50,50 @@ def __str__(self) -> str:
# See also see, Github Issue #28 https://github.com/exasol/error-reporting-python/issues/28


# ATTENTION: In the event of an exception while creating an error, we encounter a chicken-and-egg problem regarding error definitions.
# Therefore, errors created by this library must be defined "statically" within this file.
# Details should then be used to create a low-level error. The information should also be used to create/update the error-codes.json
# as part of the release preparation. This is only necessary for this library, as it is the root, other libraries should use
# the `ec` command-line tool to update and create their project specifc error-codes.json file.
LIBRARY_ERRORS = {
"E-ERP-1": {
"identifier": "E-ERP-1",
"message": "Invalid error code {{code}}.",
"messagePlaceholders": [
{
"placeholder": "code",
"description": "Error code which was causing the error."
}
],
"mitigations": ["Ensure you follow the standard error code format."],
"sourceFile": Path(__file__).name
},
"E-ERP-2": {
"identifier": "E-ERP-2",
"message": "Unknown error/exception occurred.",
"messagePlaceholders": [
{
"placeholder": "traceback",
"description": "Exception traceback which lead to the generation of this error."
}
],
"description": "An unexpected error occurred during the creation of the error",
"mitigations": [cleandoc("""
A good starting point would be to investigate the cause of the attached exception.
Trackback:
{{traceback}}
""")],
"sourceFile": Path(__file__).name
},
}


def ExaError(
code: str,
message: str,
mitigations: Union[str, List[str]],
parameters: Mapping[str, Union[str, Parameter]],
code: str,
message: str,
mitigations: Union[str, List[str]],
parameters: Mapping[str, Union[str, Parameter]],
) -> Error:
"""Create a new ExaError.
Expand All @@ -66,33 +108,42 @@ def ExaError(
Returns:
An error type containing all relevant information.
Raises:
This function should not raise under any circumstances.
In case of error, it should report it also as an error type which is returned by this function.
Still the returned error should contain a references or information about the original call.
Attention:
FIXME: Due to legacy reasons this function currently still may raise an `ValueError` (Refactoring Required).
Potential error scenarios which should taken into account are the following ones:
* E-ERP-1: Invalid error code provided
params:
* Original ErrorCode
* Original error message
* Original mitigations
* Original parameters
* E-ERP-2 Unknown error/exception occurred
params:
* exception/msg
* Original ErrorCode
* Original error message
* Original mitigations
* Original parameters
see also [Github Issue #27](https://github.com/exasol/error-reporting-python/issues/27)
"""
return Error(code, message, mitigations, parameters)
try:
return Error(code, message, mitigations, parameters)
except InvalidErrorCode:
error_code = 'E-ERP-1'
error_details = LIBRARY_ERRORS[error_code]
return Error(
code=error_details['identifier'],
message=error_details['message'],
mitigations=error_details['mitigations'],
parameters={"code": code}
)
except Exception as ex:
import traceback
tb = traceback.format_exc()
parameters = {"traceback": tb}
error_code = 'E-ERP-2'
error_details = LIBRARY_ERRORS[error_code]
return Error(
code=error_details['identifier'],
message=error_details['message'],
mitigations=error_details['mitigations'],
parameters=parameters
)


def _create_error_code_definitions(version=None):
from exasol.error.version import VERSION
version = version or VERSION
return {
"$schema": "https://schemas.exasol.com/error_code_report-1.0.0.json",
"projectName": "exasol-error-reporting",
"projectVersion": version,
"errorCodes": [code for code in LIBRARY_ERRORS.values()]
}


if __name__ == "__main__":
pass
24 changes: 16 additions & 8 deletions exasol_error_reporting_python/error_message_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@
ERROR_CODE_FORMAT = "^[FEW]-[A-Z][A-Z0-9]+(-[A-Z][A-Z0-9]+)*-[0-9]+$"


class InvalidErrorCode(ValueError):
"""
Indicates that the error code does not comply with the commonly defined error code format.
"""


class ErrorMessageBuilder:
"""
This class is a builder for Exasol error messages.
"""

def __init__(self, error_code: str):
if not re.compile(ERROR_CODE_FORMAT).match(error_code):
raise ValueError(f"{error_code} is an invalid error-code format")
raise InvalidErrorCode(f"{error_code} is an invalid error-code format")

self._error_code = error_code
self._message_builder = []
Expand Down Expand Up @@ -64,8 +70,10 @@ def ticket_mitigation(self):
:return: self of the ErrorMessageBuilder object
"""

self.mitigation("This is an internal error that should not happen. "
"Please report it by opening a GitHub issue.")
self.mitigation(
"This is an internal error that should not happen. "
"Please report it by opening a GitHub issue."
)
return self

def parameter(self, placeholder: str, value: Any, description: str = None):
Expand Down Expand Up @@ -97,7 +105,7 @@ def _add_parameters(self, text: str, arguments: Any):

self._parameter_dict = {
**self._parameter_dict,
**ParametersMapper.get_params_dict(text, arguments)
**ParametersMapper.get_params_dict(text, arguments),
}

def _replace_placeholder(self, text: str) -> str:
Expand All @@ -108,8 +116,7 @@ def _replace_placeholder(self, text: str) -> str:
:return: formatted text by replacing placeholders with arguments.
"""

return PlaceholderHandler.replace_placeholder(
text, self._parameter_dict)
return PlaceholderHandler.replace_placeholder(text, self._parameter_dict)

def __str__(self):
result = []
Expand All @@ -125,8 +132,9 @@ def __str__(self):
elif len(self._mitigations) > 1:
mitigations = ["Known mitigations:"]
for mitigation in self._mitigations:
mitigations.append("".join(
("* ", self._replace_placeholder(mitigation))))
mitigations.append(
"".join(("* ", self._replace_placeholder(mitigation)))
)
result.append("\n".join(mitigations))

return " ".join(result)
20 changes: 19 additions & 1 deletion noxconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@
from pathlib import Path
from typing import Iterable

from exasol.toolbox.nox.plugin import hookimpl


class UpdateErrorCodes:
ERROR_CODES: Path = Path(__file__).parent / "error-codes.json"

@hookimpl
def prepare_release_update_version(self, session, config, version):
import json
from exasol.error._error import _create_error_code_definitions
definitions = _create_error_code_definitions(f'{version}')
with open(UpdateErrorCodes.ERROR_CODES, 'w') as f:
json.dump(definitions, f)

@hookimpl
def prepare_release_add_files(self, session, config):
return [self.ERROR_CODES]


@dataclass(frozen=True)
class Config:
Expand All @@ -14,7 +32,7 @@ class Config:
doc: Path = Path(__file__).parent / "doc"
version_file: Path = Path(__file__).parent / "exasol" / "error" / "version.py"
path_filters: Iterable[str] = ("dist", ".eggs", "venv")
plugins = []
plugins = [UpdateErrorCodes]


PROJECT_CONFIG = Config()
Loading

0 comments on commit 0b113dc

Please sign in to comment.