Skip to content

Commit 716d219

Browse files
committed
Replace @cached_property with a @property for documentation purposes
This renames the slots previously named the same as the `@cached_property` they were made for. They now have the suffix `_cache`, so that we can name the `@property` the same as the `@cached_property`.
1 parent fb9ea67 commit 716d219

1 file changed

Lines changed: 37 additions & 1 deletion

File tree

src/attr/_make.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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+
551586
def _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

Comments
 (0)