@@ -505,6 +505,7 @@ def _make_cached_property_getattr(cached_properties, original_getattr, cls):
505505 " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):" ,
506506 " func = cached_properties.get(item)" ,
507507 " if func is not None:" ,
508+ " item = item + '_cache'" ,
508509 " result = func(self)" ,
509510 " _setter = _cached_setattr_get(self)" ,
510511 " _setter(item, result)" ,
@@ -548,6 +549,40 @@ def _make_cached_property_getattr(cached_properties, original_getattr, cls):
548549 )["__getattr__" ]
549550
550551
552+ def _make_cached_property_uncached (original_cached_property_func , cls ):
553+ """Make an ordinary :deco:`property` to replace a :deco:`cached_property`
554+
555+ This is mainly done for documentation purposes. The generated
556+ :deco:`property` won't be called, because of our custom :meth:`__getattr__`.
557+ We still want it there, so that its docstring is accessible, both to
558+ :function:`help` and to documentation generators, such as Sphinx.
559+
560+ """
561+ name = original_cached_property_func .__name__
562+ doc = original_cached_property_func .__doc__
563+ doc_lines = []
564+ if doc is not None :
565+ doc_lines = doc .splitlines (True )
566+ doc_lines [0 ] = '"""' + doc_lines [0 ]
567+ doc_lines .append ("\n " )
568+ doc_lines .append ('"""' )
569+
570+ annotation = inspect .signature (original_cached_property_func ).return_annotation
571+ if annotation is inspect .Parameter .empty :
572+ defline = f"def { name } (self):"
573+ else :
574+ defline = f"def { name } (self) -> { annotation } :"
575+ lines = [
576+ "@property" ,
577+ defline ,
578+ * (" " + line for line in doc_lines ),
579+ f" return self.__getattr__('{ name } _cache')"
580+ ]
581+ unique_filename = _generate_unique_filename (cls , original_cached_property_func )
582+ glob = {"original_cached_property" : original_cached_property_func }
583+ return _linecache_and_compile ("\n " .join (lines ), unique_filename , glob )[name ]
584+
585+
551586def _frozen_setattrs (self , name , value ):
552587 """
553588 Attached to frozen classes as __setattr__.
@@ -915,13 +950,14 @@ def _create_slots_class(self):
915950 class_annotations = _get_annotations (self ._cls )
916951 for name , func in cached_properties .items ():
917952 # Add cached properties to names for slotting.
918- names += (name ,)
953+ names += (name + '_cache' ,)
919954 # Clear out function from class to avoid clashing.
920955 del cd [name ]
921956 additional_closure_functions_to_update .append (func )
922957 annotation = inspect .signature (func ).return_annotation
923958 if annotation is not inspect .Parameter .empty :
924959 class_annotations [name ] = annotation
960+ cd [name ] = _make_cached_property_uncached (func , self ._cls )
925961
926962 original_getattr = cd .get ("__getattr__" )
927963 if original_getattr is not None :
0 commit comments