cab / lib /common /decorators.py
docs4you's picture
Upload 487 files
27867f1 verified
"""
MIT License
Copyright (C) 2023 ROCKY4546
https://github.com/rocky4546
This file is part of Cabernet
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the “Software”), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
"""
import functools
import http
import http.client
import json
import logging
import os
import re
import requests.exceptions
import sys
import socket
import time
import traceback
import urllib
import urllib.parse
import urllib.error
import urllib3
from functools import update_wrapper
def handle_url_except(f=None, timeout=None):
"""
timeout not currently used
"""
if f is None:
return functools.partial(handle_url_except, timeout=timeout)
def wrapper_func(self, *args, **kwargs):
ex_save = None
# arg0 = uri, arg1=retries
if len(args) == 0:
arg0 = 'None'
retries = 2
elif len(args) == 1:
arg0 = args[0]
retries = 2
else:
arg0 = args[0]
retries = args[1]
i = retries
is_done = 0
while i > is_done:
i -= 1
try:
x = f(self, *args, **kwargs)
return x
except UnicodeDecodeError as ex:
ex_save = ex
self.logger.info("UnicodeDecodeError in function {}(), retrying {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0), ))
except ConnectionRefusedError as ex:
ex_save = ex
self.logger.info("ConnectionRefusedError in function {}(), retrying (): {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except ConnectionResetError as ex:
ex_save = ex
self.logger.info("ConnectionResetError in function {}(), retrying {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except requests.exceptions.InvalidURL as ex:
ex_save = ex
self.logger.info("InvalidURL Error in function {}(), retrying {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except (socket.timeout, requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout) as ex:
ex_save = ex
self.logger.info("Socket Timeout Error in function {}(), retrying {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except requests.exceptions.ConnectionError as ex:
ex_save = ex
if hasattr(ex.args[0], 'reason'):
reason = ex.args[0].reason
else:
reason = None
if isinstance(reason, urllib3.exceptions.NewConnectionError):
self.logger.info("ConnectionError:ConnectionRefused in function {}(), retrying (): {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
time.sleep(2)
else:
self.logger.info("ConnectionError in function {}(), retrying (): {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except http.client.RemoteDisconnected as ex:
ex_save = ex
self.logger.info('Remote Server Disconnect Error in function {}(), retrying {} {} {}'
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except http.client.InvalidURL as ex:
url_tuple = urllib.parse.urlparse(arg0)
url_list = list(url_tuple)
url_list[2] = urllib.parse.quote(url_list[2])
url_list[3] = urllib.parse.quote(url_list[3])
url_list[4] = urllib.parse.quote(url_list[4])
url_list[5] = urllib.parse.quote(url_list[5])
new_url = urllib.parse.urlunparse(url_list)
args_list = list(args)
args_list[0] = new_url
args = tuple(args_list)
ex_save = ex
self.logger.info('InvalidURL Error, encoding and trying again. In function {}() {} {} {}'
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except (requests.exceptions.HTTPError, urllib.error.HTTPError) as ex:
ex_save = ex
self.logger.info("HTTPError in function {}(), retrying {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0), ))
except urllib.error.URLError as ex:
ex_save = ex
if isinstance(ex.reason, ConnectionRefusedError):
self.logger.info("URLError:ConnectionRefusedError in function {}(): {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
count = 5
while count > 0:
try:
x = f(self, *args, **kwargs)
return x
except urllib.error.URLError as ex2:
self.logger.debug("{} URLError:ConnectionRefusedError in function {}(): {} {} {}"
.format(count, f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
count -= 1
time.sleep(.5)
except Exception as ex3:
break
else:
self.logger.info("URLError in function {}(), retrying (): {} {} {}"
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except http.client.IncompleteRead as ex:
ex_save = ex
self.logger.info('Partial Data Error from url received in function {}(), retrying. {} {} {}'
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except ValueError as ex:
ex_save = ex
self.logger.info('ValueError in function {}(), aborting. {} {} {}'
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except (requests.exceptions.ProxyError, requests.exceptions.SSLError, \
requests.exceptions.TooManyRedirects, requests.exceptions.InvalidHeader, \
requests.exceptions.InvalidProxyURL, requests.exceptions.ChunkedEncodingError, \
requests.exceptions.ContentDecodingError, requests.exceptions.StreamConsumedError, \
requests.exceptions.RetryError, requests.exceptions.UnrewindableBodyError, \
requests.exceptions.RequestsWarning, requests.exceptions.FileModeWarning, \
requests.exceptions.RequestsDependencyWarning) as ex:
ex_save = ex
self.logger.info('General Requests Error in function {}(), retrying. {} {} {}'
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema) as ex:
ex_save = ex
self.logger.info('Missing Schema Error in function {}(), retrying. {} {} {}'
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
time.sleep(1.0)
self.logger.notice('Multiple HTTP Errors, unable to get url data, skipping {}() {} {} {}'
.format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
return None
return update_wrapper(wrapper_func, f)
def handle_json_except(f):
def wrapper_func(self, *args, **kwargs):
try:
return f(self, *args, **kwargs)
except (json.JSONDecodeError) as jsonError:
self.logger.error("JSONError in function {}(): {}".format(f.__qualname__, str(jsonError)))
return None
return update_wrapper(wrapper_func, f)
class Backup:
"""
Decorator for collecting and processing export/backup methods
"""
backup2func = {}
def __init__(self, *pattern):
self.pattern = pattern
def __call__(self, call_class_fn):
if call_class_fn is None:
return call_class_fn
else:
for p in self.pattern:
Backup.backup2func[p] = call_class_fn
return call_class_fn
@classmethod
def log_backups(cls):
logger = logging.getLogger(__name__)
for name in Backup.backup2func.keys():
logger.debug('Registering BACKUP {}'.format(name))
@classmethod
def call_backup(cls, _name, *args, **kwargs):
"""
Based on function, will create class instance and call
the function with no parameters. *args are
passed into the class constructor while **kwargs are
passed into the instance function
"""
if _name in Backup.backup2func:
fn = Backup.backup2func[_name]
module = fn.__module__
class_fn = fn.__qualname__
(cls_name, fn_name) = class_fn.split('.')
cls = vars(sys.modules[module])[cls_name]
inst = cls(*args)
inst_fn = getattr(inst, fn_name)
inst_fn(**kwargs)
return True
else:
return False
class Restore:
"""
Decorator for collecting and processing import/restore methods
"""
restore2func = {}
def __init__(self, *pattern):
self.pattern = pattern
def __call__(self, call_class_fn):
if call_class_fn is None:
return call_class_fn
else:
for p in self.pattern:
Restore.restore2func[p] = call_class_fn
return call_class_fn
@classmethod
def log_backups(cls):
logger = logging.getLogger(__name__)
for name in Restore.restore2func.keys():
logger.debug('Registering RESTORE {}'.format(name))
@classmethod
def call_restore(cls, _name, *args, **kwargs):
"""
Based on function, will create class instance and call
the function with no parameters. *args are
passed into the class constructor while **kwargs are
passed into the instance function
"""
if _name in Restore.restore2func:
fn = Restore.restore2func[_name]
module = fn.__module__
class_fn = fn.__qualname__
(cls_name, fn_name) = class_fn.split('.')
cls = vars(sys.modules[module])[cls_name]
inst = cls(*args)
inst_fn = getattr(inst, fn_name)
msg = inst_fn(**kwargs)
return msg
else:
return None
class Request:
"""
Adds urls to functions for GET and POST methods
"""
def __init__(self):
self.url2func = {}
self.method = None
def route(self, *pattern):
def wrap(func):
for p in pattern:
if p.startswith('RE:'):
p = re.compile(p.replace('RE:', ''))
self.url2func[p] = func
return func
return wrap
def log_urls(self):
logger = logging.getLogger(__name__)
for name in self.url2func.keys():
logger.debug('Registering {} URL: {}'.format(self.method, name))
def call_url(self, _webserver, _name, *args, **kwargs):
if _name in self.url2func:
self.url2func[_name](_webserver, *args, **kwargs)
return True
else:
for uri in self.url2func.keys():
if type(uri) is re.Pattern:
if len(uri.findall(_name)) > 0:
self.url2func[uri](_webserver, *args, **kwargs)
return True
return False
class GetRequest(Request):
def __init__(self):
super().__init__()
self.method = 'GET'
class PostRequest(Request):
def __init__(self):
super().__init__()
self.method = 'POST'
class FileRequest(Request):
"""
Adds HTDOCS areas to be processed by function
"""
def __init__(self):
super().__init__()
self.method = 'GET'
def call_url(self, _webserver, _name, *args, **kwargs):
for key in self.url2func.keys():
if _name.startswith(key):
self.url2func[key](_webserver, *args, **kwargs)
return True
return False
getrequest = GetRequest()
gettunerrequest = GetRequest()
postrequest = PostRequest()
filerequest = FileRequest()