#!/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