diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0d09e..34206ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.0.0-beta62 - 2020-10-17 + +### Fixed + +- Fix an issue causing exceptions not to be logged to the log file (but logged to stdout). + ## 1.0.0-beta61 - 2020-10-11 ### POTENTIALLY BREAKING CHANGE diff --git a/hyperglass/__init__.py b/hyperglass/__init__.py index af4e0bd..443e25f 100644 --- a/hyperglass/__init__.py +++ b/hyperglass/__init__.py @@ -41,16 +41,12 @@ import logging # Third Party import uvloop -import rich.traceback # Project from hyperglass.log import _get_rich from hyperglass.util import set_app_path from hyperglass.constants import METADATA -# Use Rich for traceback formatting. -rich.traceback.install() - # Set Rich as the default logging handler. logging.getLogger().handlers = [_get_rich(True)] diff --git a/hyperglass/constants.py b/hyperglass/constants.py index dd227fe..8073e43 100644 --- a/hyperglass/constants.py +++ b/hyperglass/constants.py @@ -4,7 +4,7 @@ from datetime import datetime __name__ = "hyperglass" -__version__ = "1.0.0-beta.61" +__version__ = "1.0.0-beta.62" __author__ = "Matt Love" __copyright__ = f"Copyright {datetime.now().year} Matthew Love" __license__ = "BSD 3-Clause Clear License" diff --git a/hyperglass/exceptions.py b/hyperglass/exceptions.py index 497cf92..2ff6e56 100644 --- a/hyperglass/exceptions.py +++ b/hyperglass/exceptions.py @@ -2,12 +2,17 @@ # Standard Library import json as _json -from typing import Dict, List, Union, Sequence +from typing import Dict, List, Union, Optional, Sequence + +# Third Party +from rich.console import Console # Project from hyperglass.log import log from hyperglass.constants import STATUS_CODE_MAP +console = Console() + def validation_error_message(*errors: Dict) -> str: """Parse errors return from pydantic.ValidationError.errors().""" @@ -24,14 +29,13 @@ def validation_error_message(*errors: Dict) -> str: class HyperglassError(Exception): """hyperglass base exception.""" - def __init__(self, message="", level="warning", keywords=None): - """Initialize the hyperglass base exception class. - - Keyword Arguments: - message {str} -- Error message (default: {""}) - level {str} -- Error severity (default: {"warning"}) - keywords {list} -- 'Important' keywords (default: {None}) - """ + def __init__( + self, + message: str = "", + level: str = "warning", + keywords: Optional[List[str]] = None, + ) -> None: + """Initialize the hyperglass base exception class.""" self._message = message self._level = level self._keywords = keywords or [] @@ -42,76 +46,46 @@ class HyperglassError(Exception): else: log.info(repr(self)) - def __str__(self): - """Return the instance's error message. + console.print_exception(extra_lines=6) - Returns: - {str} -- Error Message - """ + def __str__(self) -> str: + """Return the instance's error message.""" return self._message - def __repr__(self): - """Return the instance's severity & error message in a string. - - Returns: - {str} -- Error message with code - """ + def __repr__(self) -> str: + """Return the instance's severity & error message in a string.""" return f"[{self.level.upper()}] {self._message}" - def dict(self): - """Return the instance's attributes as a dictionary. - - Returns: - {dict} -- Exception attributes in dict - """ + def dict(self) -> Dict: + """Return the instance's attributes as a dictionary.""" return { "message": self._message, "level": self._level, "keywords": self._keywords, } - def json(self): - """Return the instance's attributes as a JSON object. - - Returns: - {str} -- Exception attributes as JSON - """ + def json(self) -> str: + """Return the instance's attributes as a JSON object.""" return _json.dumps(self.__dict__()) @property - def message(self): - """Return the instance's `message` attribute. - - Returns: - {str} -- Error Message - """ + def message(self) -> str: + """Return the instance's `message` attribute.""" return self._message @property - def level(self): - """Return the instance's `level` attribute. - - Returns: - {str} -- Alert name - """ + def level(self) -> str: + """Return the instance's `level` attribute.""" return self._level @property - def keywords(self): - """Return the instance's `keywords` attribute. - - Returns: - {list} -- Keywords List - """ + def keywords(self) -> List[str]: + """Return the instance's `keywords` attribute.""" return self._keywords @property - def status_code(self): - """Return HTTP status code based on level level. - - Returns: - {int} -- HTTP Status Code - """ + def status_code(self) -> int: + """Return HTTP status code based on level level.""" return STATUS_CODE_MAP.get(self._level, 500) @@ -120,14 +94,10 @@ class _UnformattedHyperglassError(HyperglassError): _level = "warning" - def __init__(self, unformatted_msg="", level=None, **kwargs): - """Format error message with keyword arguments. - - Keyword Arguments: - message {str} -- Error message (default: {""}) - level {str} -- Error severity (default: {"warning"}) - keywords {list} -- 'Important' keywords (default: {None}) - """ + def __init__( + self, unformatted_msg: str = "", level: Optional[str] = None, **kwargs + ) -> None: + """Format error message with keyword arguments.""" self._message = unformatted_msg.format(**kwargs) self._level = level or self._level self._keywords = list(kwargs.values()) @@ -140,7 +110,7 @@ class _PredefinedHyperglassError(HyperglassError): _message = "undefined" _level = "warning" - def __init__(self, level=None, **kwargs): + def __init__(self, level: Optional[str] = None, **kwargs) -> None: self._fmt_msg = self._message.format(**kwargs) self._level = level or self._level self._keywords = list(kwargs.values()) @@ -152,7 +122,7 @@ class _PredefinedHyperglassError(HyperglassError): class ConfigInvalid(HyperglassError): """Raised when a config item fails type or option validation.""" - def __init__(self, errors: List) -> None: + def __init__(self, errors: List[str]) -> None: """Parse Pydantic ValidationError.""" super().__init__(message=validation_error_message(*errors)) @@ -219,7 +189,7 @@ class ParsingError(_UnformattedHyperglassError): unformatted_msg: Union[Sequence[Dict], str], level: str = "danger", **kwargs, - ): + ) -> None: """Format error message with keyword arguments.""" if isinstance(unformatted_msg, Sequence): self._message = validation_error_message(*unformatted_msg) diff --git a/pyproject.toml b/pyproject.toml index 936341c..f37e1b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ license = "BSD-3-Clause-Clear" name = "hyperglass" readme = "README.md" repository = "https://github.com/checktheroads/hyperglass" -version = "1.0.0-beta.61" +version = "1.0.0-beta.62" [tool.poetry.scripts] hyperglass = "hyperglass.console:CLI"