Source code for noctua.utils.check

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Collection of decorators to check several type of errors in
specific types of functions.

"""

# System modules
import socket

# Third-party modules
import requests
from pyvantagepro.device import NoDeviceException

# Other templates
from ..utils.logger import log

# class ManualRetry(Exception):
#     '''
#     Manually raise an exception, so it is possible to handle a retry
#     '''


[docs] def content_errors(content): ''' Decorator for handling exceptions. Put it on top of the content() method in the shinsapp class. ''' def content_errors_inner(*args, **kwargs): ''' Modifies the content() function ''' this = args[0] # It's the "self" try: ''' This is the content method in basetemplate which is implemented in each template. If all goes well, it is executed smoothly. ''' content(*args, **kwargs) except Exception as e: ''' If an error occurs, the template is paused at the beginning of the following paragraph. ''' log.error(f"CONTENT: exception {e}") this.paused = True return content_errors_inner
[docs] def request_errors(func): ''' Decorator for handling exceptions on a class method. Put it on top of the class method. ''' def request_errors_inner(*args, **kwargs): ''' Modifies the func() method. ''' this = args[0] # it's the "self" name = str(args[0].__class__) try: this.error = [] res = func(*args, **kwargs) return res except requests.exceptions.HTTPError as e: res = e.response if res.status_code == 400: # API returns body like: '0x8000100a\r\nParameter(s) missing.\r\n' try: # Attempt to parse the API-specific error body_lines = res.text.strip().split('\r\n') error_code = body_lines[0] error_message = body_lines[1] if len(body_lines) > 1 else "No error message provided." msg = f"{name}: Bad Request - API Error {error_code}: {error_message}" log.error(msg) this.error.append(msg) except (IndexError, AttributeError): # Fallback for unexpected 400 error format msg = f"{name}: Bad Request with unparsable body." log.error(msg) log.error(f"Raw response: {res.text}") this.error.append(msg) elif res.status_code == 500: msg = f"{name}: Internal Server Error on device." log.error(msg) log.error(f"Reason: {res.reason}") log.error(f"Response: {res.text}") this.error.append(msg) this.error.append(res.text) else: # 404 Not Found, etc. msg = f"{name}: HTTP Error - {e}" log.error(msg) this.error.append(str(e)) return except (requests.exceptions.Timeout, requests.exceptions.ConnectTimeout) as e: msg = f"{name}: Timeout or Connection timeout!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return [] except requests.exceptions.TooManyRedirects as e: msg = f"{name}: Bad request!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except ConnectionRefusedError as e: msg = f"{name}: Connection refused!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except requests.exceptions.ConnectionError as e: msg = f"{name}: Connection error!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return [] except AttributeError as e: msg = f"{name}: No data!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except AssertionError as e: msg = f"{name}: Probably cabinet not ready!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except TypeError as e: msg = f"{name}: Probably subsystem not responding!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except requests.exceptions.RequestException as e: msg = f"{name}: Generic request exception!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) raise SystemExit(e) from e except Exception as e: msg = f"{name}: Not handled error!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) raise SystemExit(e) from e return request_errors_inner
[docs] def telnet_errors(func): ''' Decorator for handling exceptions on a class method. Put it on top of the class method. ''' def telnet_errors_inner(*args, **kwargs): ''' Modifies the func() method. ''' this = args[0] # it's the "self" name = str(args[0].__class__) try: this.error = [] return func(*args, **kwargs) except socket.gaierror as e: msg = f"{name}: Server not found!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except socket.timeout as e: msg = f"{name}: Server timeout!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except AttributeError as e: msg = f"No attribute with this name in {name}" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return except BrokenPipeError as e: msg = f"{name}: Probably sent request to telnet while waiting previous response" log.debug(msg) log.debug(e) log.error(e) except IOError as e: msg = f"{name}: Probably failed getting info from telnet" log.debug(msg) log.debug(e) log.error(e) except Exception as e: msg = f"{name}: Not handled error" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) raise SystemExit(e) return telnet_errors_inner
[docs] def ascom_errors(func): ''' Decorator for handling exceptions on a class method. Put it on top of the class method. ''' def ascom_errors_inner(*args, **kwargs): ''' Modifies the func() method. ''' this = args[0] # it's the "self" name = str(args[0].__class__) try: this.error = [] return func(*args, **kwargs) except Exception as e: msg = f"{name}: Not handled error" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) raise SystemExit(e) return ascom_errors_inner
[docs] def meteo_errors(func): ''' Decorator for handling exceptions on a class method. Put it on top of the class method. ''' def meteo_errors_inner(*args, **kwargs): ''' Modifies the func() method. ''' this = args[0] # it's the "self" name = str(args[0].__class__) try: this.error = [] return func(*args, **kwargs) except NoDeviceException as e: msg = f"{name}: Cannot connect to meteo station" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) except BrokenPipeError as e: msg = f"{name}: Probably sent request to meteo station while waiting previous response" log.debug(msg) log.debug(e) except IOError as e: msg = f"{name}: Probably failed getting info from Meteo station" log.debug(msg) log.debug(e) except Exception as e: msg = f"{name}: Not handled error" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) raise SystemExit(e) return meteo_errors_inner
[docs] def socket_errors(func): ''' Decorator for handling exceptions on a class method. Put it on top of the class method. ''' def socket_errors_inner(*args, **kwargs): ''' Modifies the func() method. ''' this = args[0] # it's the "self" name = str(args[0].__class__) try: this.error = [] return func(*args, **kwargs) except Exception as e: msg = f"{name}: Not handled exception!" log.error(msg) log.error(e) this.error.append(msg) this.error.append(str(e)) return