# Cerberror

[](https://www.python.org/)
[](https://black.readthedocs.io/en/stable/)
[](https://github.com/pbrus/cerberror/blob/master/LICENSE)
**Cerberror** was designed to customize and handle errors generated by the validator of [Cerberus](https://docs.python-cerberus.org/).
## How it works?
Let's start from checking errors generated by Cerberus:
```python
>>> v = Validator({'params': {'type': 'dict', 'schema': {'var1': {'type': 'integer'}, 'var2': {'allowed': [7, 8, 9]}}}})
>>> v.validate({'params': {'var1': 'Hello World!', 'var2': 3.14}})
>>> v.errors
{'params': [{'var1': ['must be of integer type'], 'var2': ['unallowed value 3.14']}]}
```
At least two problems occur when we want to inform a user what went wrong:
- predetermined error messages,
- unclear path to wrong variable.
Let's define new messages in `msgs.txt` file:
```
('params', 'var1') 36 "{{value}} is not an integer!"
('params', 'var2') 68 "{{value}} not found in {{constraint}}..."
```
where the columns contain:
1. A path to the element defined by a tuple. Note that one-element tuple must be defined as `('some_param',)`, not `'some_param',`.
2. An error code from the [API documentation](https://docs.python-cerberus.org/en/stable/api.html#error-codes). A particular path can have many rules which implies many records in `msgs.txt` for that path.
3. A user-defined message. Optionally we can refer to [attributes](https://docs.python-cerberus.org/en/stable/api.html#cerberus.errors.ValidationError) of a particular error using `{{attr}}` expressions. Note that a message must be defined between two `"`, not `'`.
To get new messages we use `Translator` object:
```python
>>> from cerberror import Translator
>>> tr = Translator(v, 'msgs.txt')
>>> tr.translate()
{'params -> var2': ['3.14 not found in [7, 8, 9]...'], 'params -> var1': ['Hello World! is not an integer!']}
```
## Installation
To install the package please type from the command line:
```bash
$ pip3 install cerberror
```
## Features
**Cerberror** has a few features which facilitate the usage of it.
### Updates
Just like the validator of Cerberus, `Translator` object can be updated at any time:
```python
>>> tr = Translator(validator, 'messages.txt')
>>> tr.validator = another_validator
>>> tr.path_to_file = 'another_messages.txt'
```
This feature is particularly useful when we want to update files with messages on the fly. It could be used when we need to change language. Just define a proper file:
```
('params', 'var1') 36 "{{value}} ist keine ganze Zahl!"
('params', 'var2') 68 "{{value}} nicht unter {{constraint}} gefunden..."
```
### Comments
Comments can be added to files with messages. These files are parsing with the usage of the regular expressions. Valid records are those defined in [How it works?](#how-it-works) section. This means that everything else is treated as a comment. However, I recommend to use `#` to mark where they start.
```
# Comment.
('params', 'var1') 36 "{{value}} is not an integer!" # Inline comment.
('params', 'var2') 68 "{{value}} not found in {{constraint}}..."
```
### Paths
The returned paths are joined with usage of the default value `' -> '`. This behaviour can be changed easily:
```python
>>> tr = Translator(v, 'msgs.txt')
>>> tr.translate('_')
{'params_var2': ['3.14 not found in [7, 8, 9]...'], 'params_var1': ['Hello World! is not an integer!']}
```
### Returns
If the translation will finish successfully, the returned value will be a dictionary composed of `path:message(s)` pairs. Otherwise, `Translator` will return untouched errors generated by Cerberus. We can check the status of the translation using `any_error` property:
```python
>>> tr.any_error
False
>>> tr.errors
{'params -> var2': ['3.14 not found in [7, 8, 9]...'], 'params -> var1': ['Hello World! is not an integer!']}
...
>>> tr.any_error
True
>>> tr.errors
{'params': [{'var1': ['must be of integer type'], 'var2': ['unallowed value 3.14']}]} # v.errors
```
### Internal errors
Internal errors can be inspected to find out what went wrong. The assumption was that **Cerberror** must not interrupt the translation process. If internal errors occur, `any_error` property will return `True`. Internal errors are always store in `error_list` property. For example:
```
# foo and bar attributes don't exist
('params', 'var1') 36 "{{foo}} is not an integer!"
('params', 'var2') 68 "{{value}} not found in {{bar}}..."
```
```python
>>> tr = Translator(v, 'msgs.txt')
>>> tr.translate()
{'params': [{'var1': ['must be of integer type'], 'var2': ['unallowed value 3.14']}]}
>>> tr.any_error
True
>>> tr.error_list
["Invalid expression '{{bar}}' in file 'msgs.txt'", "Invalid expression '{{foo}}' in file 'msgs.txt'"]
```
## Contribution
New feature, bugs? Issues and pull requests are welcome.
## License
**Cerberror** is licensed under the [MIT license](http://opensource.org/licenses/MIT).