Repository: maxfischer2781/asyncstdlib

Issue:
Check functools.cached_property return value nesting
The handling of the return value in `functools.cached_property` appears to be too deeply/shallowly nested with awaitables. Wrapping of the original method means:

- `__wrapped__` is of type `(Self) -> await T`
- `value = await self.__wrapped__(instance)` is of type `T`
- `attributes[self._name] = AwaitableValue(value)` is of type `await T`

Now here comes the problem: The helper method has two `return` paths, one for `return value` (``... -> T``) and one for `return attributes[self._name]` (``... -> await T``)! It cannot be correct for both paths.

I assume the latter path is dead in the unit-tests, and I'm somewhat sure it cannot ever be reached. Either way, this should be tested and if necessary fixed or cleaned up.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) Report
> Merging [#49](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) (066f09b) into [master](https://codecov.io/gh/maxfischer2781/asyncstdlib/commit/2b7038d1f3e93c39ef5e6490e3566b79b632febe?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) (2b7038d) will **increase** coverage by `0.05%`.
> The diff coverage is `60.00%`.

[![Impacted file tree graph](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49/graphs/tree.svg?width=650&height=150&src=pr&token=frT6svSwUb&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer)](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer)

```diff
@@            Coverage Diff             @@
##           master      #49      +/-   ##
==========================================
+ Coverage   98.77%   98.83%   +0.05%     
==========================================
  Files          16       16              
  Lines        1632     1627       -5     
  Branches      188      187       -1     
==========================================
- Hits         1612     1608       -4     
+ Misses         14       13       -1     
  Partials        6        6              
```


| [Impacted Files](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer) | Coverage Δ | |
|---|---|---|
| [unittests/test\_functools.py](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer#diff-dW5pdHRlc3RzL3Rlc3RfZnVuY3Rvb2xzLnB5) | `94.59% <0.00%> (+0.63%)` | :arrow_up: |
| [asyncstdlib/functools.py](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer#diff-YXN5bmNzdGRsaWIvZnVuY3Rvb2xzLnB5) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer). Last update [2b7038d...066f09b](https://codecov.io/gh/maxfischer2781/asyncstdlib/pull/49?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Max+Fischer).


Patch:
diff --git a/asyncstdlib/functools.py b/asyncstdlib/functools.py
index 5bd874c..1e33091 100644
--- a/asyncstdlib/functools.py
+++ b/asyncstdlib/functools.py
@@ -116,14 +116,9 @@ class CachedProperty:
         return self._get_attribute(instance)
 
     async def _get_attribute(self, instance) -> T:
-        attributes = instance.__dict__
-        try:
-            return attributes[self._name]
-        except KeyError:
-            value = await self.__wrapped__(instance)
-            if self._name not in attributes:
-                attributes[self._name] = AwaitableValue(value)
-            return value
+        value = await self.__wrapped__(instance)
+        instance.__dict__[self._name] = AwaitableValue(value)
+        return value
 
 
 cached_property = CachedProperty


Test patch:
diff --git a/unittests/test_functools.py b/unittests/test_functools.py
index d8257f5..9d13530 100644
--- a/unittests/test_functools.py
+++ b/unittests/test_functools.py
@@ -35,12 +35,11 @@ async def test_cache_property_nodict():
             __slots__ = "a", "b"
 
             def __init__(self, a, b):
-                self.a = a
-                self.b = b
+                pass  # pragma: no cover
 
             @a.cached_property
             async def total(self):
-                return self.a + self.b
+                pass  # pragma: no cover
 
 
 @multi_sync
@@ -62,7 +61,7 @@ async def test_cache_property_order():
     val = Value(0)
     await Schedule(check_increment(5), check_increment(12), check_increment(1337))
     assert (await val.cached) != 0
-    assert (await val.cached) == 5  # first value fetched
+    assert (await val.cached) == 1337  # last value fetched
 
 
 @sync