168 lines
5.9 KiB
Python
168 lines
5.9 KiB
Python
import ast
|
|
import os
|
|
import logging
|
|
from email._policybase import _PolicyBase
|
|
from odoo import MIN_PY_VERSION
|
|
from shutil import copyfileobj
|
|
from types import CodeType
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
try:
|
|
import num2words
|
|
from .num2words_patch import Num2Word_AR_Fixed
|
|
except ImportError:
|
|
_logger.warning("num2words is not available, Arabic number to words conversion will not work")
|
|
num2words = None
|
|
|
|
from urllib3 import PoolManager
|
|
from werkzeug.datastructures import FileStorage, MultiDict
|
|
from werkzeug.routing import Rule
|
|
from werkzeug.wrappers import Request, Response
|
|
|
|
from .json import scriptsafe
|
|
|
|
try:
|
|
from stdnum import util
|
|
except ImportError:
|
|
util = None
|
|
|
|
try:
|
|
from xlrd import xlsx
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
from lxml import etree
|
|
# xlrd.xlsx supports defusedxml, defusedxml's etree interface is broken
|
|
# (missing ElementTree and thus ElementTree.iter) which causes a fallback to
|
|
# Element.getiterator(), triggering a warning before 3.9 and an error from 3.9.
|
|
#
|
|
# We have defusedxml installed because zeep has a hard dep on defused and
|
|
# doesn't want to drop it (mvantellingen/python-zeep#1014).
|
|
#
|
|
# Ignore the check and set the relevant flags directly using lxml as we have a
|
|
# hard dependency on it.
|
|
xlsx.ET = etree
|
|
xlsx.ET_has_iterparse = True
|
|
xlsx.Element_has_iter = True
|
|
|
|
FileStorage.save = lambda self, dst, buffer_size=1<<20: copyfileobj(self.stream, dst, buffer_size)
|
|
|
|
|
|
def _multidict_deepcopy(self, memo=None):
|
|
return orig_deepcopy(self)
|
|
|
|
|
|
orig_deepcopy = MultiDict.deepcopy
|
|
MultiDict.deepcopy = _multidict_deepcopy
|
|
|
|
Request.json_module = Response.json_module = scriptsafe
|
|
|
|
get_func_code = getattr(Rule, '_get_func_code', None)
|
|
if get_func_code:
|
|
@staticmethod
|
|
def _get_func_code(code, name):
|
|
assert isinstance(code, CodeType)
|
|
return get_func_code(code, name)
|
|
Rule._get_func_code = _get_func_code
|
|
|
|
orig_literal_eval = ast.literal_eval
|
|
|
|
def literal_eval(expr):
|
|
# limit the size of the expression to avoid segmentation faults
|
|
# the default limit is set to 100KiB
|
|
# can be overridden by setting the ODOO_LIMIT_LITEVAL_BUFFER buffer_size_environment variable
|
|
|
|
buffer_size = 102400
|
|
buffer_size_env = os.getenv("ODOO_LIMIT_LITEVAL_BUFFER")
|
|
|
|
if buffer_size_env:
|
|
if buffer_size_env.isdigit():
|
|
buffer_size = int(buffer_size_env)
|
|
else:
|
|
_logger.error("ODOO_LIMIT_LITEVAL_BUFFER has to be an integer, defaulting to 100KiB")
|
|
|
|
if isinstance(expr, str) and len(expr) > buffer_size:
|
|
raise ValueError("expression can't exceed buffer limit")
|
|
|
|
return orig_literal_eval(expr)
|
|
|
|
ast.literal_eval = literal_eval
|
|
|
|
if MIN_PY_VERSION >= (3, 12):
|
|
raise RuntimeError("The num2words monkey patch is obsolete. Bump the version of the library to the latest available in the official package repository, if it hasn't already been done, and remove the patch.")
|
|
|
|
if num2words:
|
|
num2words.CONVERTER_CLASSES["ar"] = Num2Word_AR_Fixed()
|
|
|
|
_soap_clients = {}
|
|
|
|
|
|
def new_get_soap_client(wsdlurl, timeout=30):
|
|
# stdnum library does not set the timeout for the zeep Transport class correctly
|
|
# (timeout is to fetch the wsdl and operation_timeout is to perform the call),
|
|
# requiring us to monkey patch the get_soap_client function.
|
|
# Can be removed when https://github.com/arthurdejong/python-stdnum/issues/444 is
|
|
# resolved and the version of the dependency is updated.
|
|
# The code is a copy of the original apart for the line related to the Transport class.
|
|
# This was done to keep the code as similar to the original and to reduce the possibility
|
|
# of introducing import errors, even though some imports are not in the requirements.
|
|
# See https://github.com/odoo/odoo/pull/173359 for a more thorough explanation.
|
|
if (wsdlurl, timeout) not in _soap_clients:
|
|
try:
|
|
from zeep.transports import Transport
|
|
transport = Transport(operation_timeout=timeout, timeout=timeout) # operational_timeout added here
|
|
from zeep import CachingClient
|
|
client = CachingClient(wsdlurl, transport=transport).service
|
|
except ImportError:
|
|
# fall back to non-caching zeep client
|
|
try:
|
|
from zeep import Client
|
|
client = Client(wsdlurl, transport=transport).service
|
|
except ImportError:
|
|
# other implementations require passing the proxy config
|
|
try:
|
|
from urllib import getproxies
|
|
except ImportError:
|
|
from urllib.request import getproxies
|
|
# fall back to suds
|
|
try:
|
|
from suds.client import Client
|
|
client = Client(
|
|
wsdlurl, proxy=getproxies(), timeout=timeout).service
|
|
except ImportError:
|
|
# use pysimplesoap as last resort
|
|
try:
|
|
from pysimplesoap.client import SoapClient
|
|
client = SoapClient(
|
|
wsdl=wsdlurl, proxy=getproxies(), timeout=timeout)
|
|
except ImportError:
|
|
raise ImportError(
|
|
'No SOAP library (such as zeep) found')
|
|
_soap_clients[(wsdlurl, timeout)] = client
|
|
return _soap_clients[(wsdlurl, timeout)]
|
|
|
|
|
|
if util:
|
|
util.get_soap_client = new_get_soap_client
|
|
|
|
|
|
def pool_init(self, *args, **kwargs):
|
|
orig_pool_init(self, *args, **kwargs)
|
|
self.pool_classes_by_scheme = {**self.pool_classes_by_scheme}
|
|
|
|
|
|
orig_pool_init = PoolManager.__init__
|
|
PoolManager.__init__ = pool_init
|
|
|
|
|
|
def policy_clone(self, **kwargs):
|
|
for arg in kwargs:
|
|
if arg.startswith("_") or "__" in arg:
|
|
raise AttributeError(f"{self.__class__.__name__!r} object has no attribute {arg!r}")
|
|
return orig_policy_clone(self, **kwargs)
|
|
|
|
|
|
orig_policy_clone = _PolicyBase.clone
|
|
_PolicyBase.clone = policy_clone
|