|
|
# Copyright (C) 2016-2024 RhodeCode GmbH
|
|
|
#
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
# it under the terms of the GNU Affero General Public License, version 3
|
|
|
# (only), as published by the Free Software Foundation.
|
|
|
#
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
# GNU General Public License for more details.
|
|
|
#
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
#
|
|
|
# This program is dual-licensed. If you wish to learn more about the
|
|
|
# RhodeCode Enterprise Edition, including its added features, Support services,
|
|
|
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
|
|
|
|
|
"""
|
|
|
Compatibility patches.
|
|
|
|
|
|
Please keep the following principles in mind:
|
|
|
|
|
|
* Keep imports local, so that importing this module does not cause too many
|
|
|
side effects by itself.
|
|
|
|
|
|
* Try to make patches idempotent, calling them multiple times should not do
|
|
|
harm. If that is not possible, ensure that the second call explodes.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
def inspect_formatargspec():
|
|
|
|
|
|
import inspect
|
|
|
from inspect import formatannotation
|
|
|
|
|
|
def backport_inspect_formatargspec(
|
|
|
args, varargs=None, varkw=None, defaults=None,
|
|
|
kwonlyargs=(), kwonlydefaults={}, annotations={},
|
|
|
formatarg=str,
|
|
|
formatvarargs=lambda name: '*' + name,
|
|
|
formatvarkw=lambda name: '**' + name,
|
|
|
formatvalue=lambda value: '=' + repr(value),
|
|
|
formatreturns=lambda text: ' -> ' + text,
|
|
|
formatannotation=formatannotation):
|
|
|
"""Copy formatargspec from python 3.7 standard library.
|
|
|
Python 3 has deprecated formatargspec and requested that Signature
|
|
|
be used instead, however this requires a full reimplementation
|
|
|
of formatargspec() in terms of creating Parameter objects and such.
|
|
|
Instead of introducing all the object-creation overhead and having
|
|
|
to reinvent from scratch, just copy their compatibility routine.
|
|
|
Utimately we would need to rewrite our "decorator" routine completely
|
|
|
which is not really worth it right now, until all Python 2.x support
|
|
|
is dropped.
|
|
|
"""
|
|
|
|
|
|
def formatargandannotation(arg):
|
|
|
result = formatarg(arg)
|
|
|
if arg in annotations:
|
|
|
result += ': ' + formatannotation(annotations[arg])
|
|
|
return result
|
|
|
|
|
|
specs = []
|
|
|
if defaults:
|
|
|
firstdefault = len(args) - len(defaults)
|
|
|
for i, arg in enumerate(args):
|
|
|
spec = formatargandannotation(arg)
|
|
|
if defaults and i >= firstdefault:
|
|
|
spec = spec + formatvalue(defaults[i - firstdefault])
|
|
|
specs.append(spec)
|
|
|
if varargs is not None:
|
|
|
specs.append(formatvarargs(formatargandannotation(varargs)))
|
|
|
else:
|
|
|
if kwonlyargs:
|
|
|
specs.append('*')
|
|
|
if kwonlyargs:
|
|
|
for kwonlyarg in kwonlyargs:
|
|
|
spec = formatargandannotation(kwonlyarg)
|
|
|
if kwonlydefaults and kwonlyarg in kwonlydefaults:
|
|
|
spec += formatvalue(kwonlydefaults[kwonlyarg])
|
|
|
specs.append(spec)
|
|
|
if varkw is not None:
|
|
|
specs.append(formatvarkw(formatargandannotation(varkw)))
|
|
|
result = '(' + ', '.join(specs) + ')'
|
|
|
if 'return' in annotations:
|
|
|
result += formatreturns(formatannotation(annotations['return']))
|
|
|
return result
|
|
|
|
|
|
# NOTE: inject for python3.11
|
|
|
inspect.formatargspec = backport_inspect_formatargspec
|
|
|
return inspect
|
|
|
|
|
|
|
|
|
def inspect_getargspec():
|
|
|
"""
|
|
|
Pyramid rely on inspect.getargspec to lookup the signature of
|
|
|
view functions. This is not compatible with cython, therefore we replace
|
|
|
getargspec with a custom version.
|
|
|
Code is inspired by the inspect module from Python-3.4
|
|
|
"""
|
|
|
import inspect
|
|
|
|
|
|
def _isCython(func):
|
|
|
"""
|
|
|
Private helper that checks if a function is a cython function.
|
|
|
"""
|
|
|
return func.__class__.__name__ == 'cython_function_or_method'
|
|
|
|
|
|
def unwrap(func):
|
|
|
"""
|
|
|
Get the object wrapped by *func*.
|
|
|
|
|
|
Follows the chain of :attr:`__wrapped__` attributes returning the last
|
|
|
object in the chain.
|
|
|
|
|
|
*stop* is an optional callback accepting an object in the wrapper chain
|
|
|
as its sole argument that allows the unwrapping to be terminated early
|
|
|
if the callback returns a true value. If the callback never returns a
|
|
|
true value, the last object in the chain is returned as usual. For
|
|
|
example, :func:`signature` uses this to stop unwrapping if any object
|
|
|
in the chain has a ``__signature__`` attribute defined.
|
|
|
|
|
|
:exc:`ValueError` is raised if a cycle is encountered.
|
|
|
"""
|
|
|
f = func # remember the original func for error reporting
|
|
|
memo = {id(f)} # Memoise by id to tolerate non-hashable objects
|
|
|
while hasattr(func, '__wrapped__'):
|
|
|
func = func.__wrapped__
|
|
|
id_func = id(func)
|
|
|
if id_func in memo:
|
|
|
raise ValueError(f'wrapper loop when unwrapping {f!r}')
|
|
|
memo.add(id_func)
|
|
|
return func
|
|
|
|
|
|
def custom_getargspec(func):
|
|
|
"""
|
|
|
Get the names and default values of a function's arguments.
|
|
|
|
|
|
A tuple of four things is returned: (args, varargs, varkw, defaults).
|
|
|
'args' is a list of the argument names (it may contain nested lists).
|
|
|
'varargs' and 'varkw' are the names of the * and ** arguments or None.
|
|
|
'defaults' is an n-tuple of the default values of the last n arguments.
|
|
|
"""
|
|
|
|
|
|
func = unwrap(func)
|
|
|
|
|
|
if inspect.ismethod(func):
|
|
|
func = func.im_func
|
|
|
if not inspect.isfunction(func):
|
|
|
if not _isCython(func):
|
|
|
raise TypeError('{!r} is not a Python or Cython function'
|
|
|
.format(func))
|
|
|
args, varargs, varkw = inspect.getargs(func.func_code)
|
|
|
return inspect.ArgSpec(args, varargs, varkw, func.func_defaults)
|
|
|
|
|
|
# NOTE: inject for python3.11
|
|
|
inspect.getargspec = inspect.getfullargspec
|
|
|
|
|
|
return inspect
|
|
|
|
|
|
|
|
|
def repoze_sendmail_lf_fix():
|
|
|
from repoze.sendmail import encoding
|
|
|
from email.policy import SMTP
|
|
|
|
|
|
encoding.encode_message = lambda message, *args, **kwargs: message.as_bytes(policy=SMTP)
|
|
|
|