Source code for gavo.utils.monkeypatch

"""
This module contains monkey patches for the python standard library
where we want to use features beyond the current base line (python 3.7).

It is imported early on by utils.__init__, so anything that is in DaCHS
should be able to rely on these patches.
"""

#c Copyright 2008-2023, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


import functools
import threading

if not hasattr(functools, "cached_property"): # pragma: no cover
	functools._NOT_FOUND = object()

	class cached_property:
		def __init__(self, func):
			self.func = func
			self.attrname = None
			self.__doc__ = func.__doc__
			self.lock = threading.RLock()

		def __set_name__(self, owner, name):
			if self.attrname is None:
				self.attrname = name
			elif name != self.attrname:
				raise TypeError(
					"Cannot assign the same cached_property to two different names "
					f"({self.attrname!r} and {name!r})."
				)

		def __get__(self, instance, owner=None):
			if instance is None:
				return self
			if self.attrname is None:
				raise TypeError(
					"Cannot use cached_property instance without calling __set_name__ on it.")
			try:
				cache = instance.__dict__
			except AttributeError:  # not all objects have __dict__ (e.g. class defines slots)
				msg = (
					f"No '__dict__' attribute on {type(instance).__name__!r} "
					f"instance to cache {self.attrname!r} property."
				)
				raise TypeError(msg) from None
			val = cache.get(self.attrname, functools._NOT_FOUND)
			if val is functools._NOT_FOUND:
				with self.lock:
					# check if another thread filled cache while we awaited lock
					val = cache.get(self.attrname, functools._NOT_FOUND)
					if val is functools._NOT_FOUND:
						val = self.func(instance)
						try:
							cache[self.attrname] = val
						except TypeError:
							msg = (
								f"The '__dict__' attribute on {type(instance).__name__!r} instance "
								f"does not support item assignment for caching {self.attrname!r} property."
							)
							raise TypeError(msg) from None
			return val

	functools.cached_property = cached_property


# Work around a rather severe openssl bug leading to presumably
# exploitable crashes https://twistedmatrix.com/trac/ticket/9764
from OpenSSL import SSL

[docs]def patch_openssl(): original = SSL.Context.__init__ def newInit(self, method): original(self, method) self.set_session_cache_mode(SSL.SESS_CACHE_OFF) SSL.Context.__init__ = newInit
patch_openssl()