| 
									
										
										
										
											2022-03-12 15:37:45 +01:00
										 |  |  | # SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | # lint: pylint | 
					
						
							|  |  |  | # pyright: basic | 
					
						
							|  |  |  | """Module for backward compatibility.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | # pylint: disable=C,R | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-28 13:02:56 +02:00
										 |  |  | __all__ = ('cached_property',) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-12 15:37:45 +01:00
										 |  |  | try: | 
					
						
							| 
									
										
										
										
											2022-07-28 13:02:56 +02:00
										 |  |  |     from functools import cached_property  # type: ignore | 
					
						
							| 
									
										
										
										
											2022-03-12 15:37:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # cache_property has been added in py3.8 [1] | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # To support cache_property in py3.7 the implementation from 3.8 has been | 
					
						
							|  |  |  |     # copied here.  This code can be cleanup with EOL of py3.7. | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # [1] https://docs.python.org/3/library/functools.html#functools.cached_property | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     from threading import RLock | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _NOT_FOUND = object() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class cached_property: | 
					
						
							|  |  |  |         def __init__(self, func): | 
					
						
							|  |  |  |             self.func = func | 
					
						
							|  |  |  |             self.attrname = None | 
					
						
							|  |  |  |             self.__doc__ = func.__doc__ | 
					
						
							|  |  |  |             self.lock = 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, _NOT_FOUND) | 
					
						
							|  |  |  |             if val is _NOT_FOUND: | 
					
						
							|  |  |  |                 with self.lock: | 
					
						
							|  |  |  |                     # check if another thread filled cache while we awaited lock | 
					
						
							|  |  |  |                     val = cache.get(self.attrname, _NOT_FOUND) | 
					
						
							|  |  |  |                     if val is _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 |