-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide aclose() / close() for classes requiring lifetime management #2898
Conversation
dd3e8c3
to
2bd38cc
Compare
4db71b9
to
7fd7e2d
Compare
@kristjanvalur You mentioned deprecating in your PR (and thank you). I'm 100% on board for marking as deprecated the old functions - that way that can be safely removed in future version unknown. Can you? I might argue that we (@dvora-h) should define in our CONTRIBUTING.md or elsewhere how and when we'll deprecate an interface |
docs/examples/asyncio_examples.ipynb
Outdated
@@ -41,7 +41,7 @@ | |||
"\n", | |||
"connection = redis.Redis()\n", | |||
"print(f\"Ping successful: {await connection.ping()}\")\n", | |||
"await connection.close()" | |||
"await connection.aclose()" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for taking the time to update the examples too!
@@ -516,6 +515,12 @@ async def close(self, close_connection_pool: Optional[bool] = None) -> None: | |||
): | |||
await self.connection_pool.disconnect() | |||
|
|||
async def close(self, close_connection_pool: Optional[bool] = None) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i.e deprecating here - as yo mentioned
|
||
def __del__(self): | ||
if self.connection: | ||
self.connection.clear_connect_callbacks() | ||
|
||
async def reset(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now removes a public method named reset - something we've shied away from doing. WDYT about keeping it defined, and also marking as deprecated? At the very least we should mark as breakingchange and then not merge to 5.1 otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think you will find that I left it in place. reset()
is an alias for aclose()
, same as close()
If you can provide me with a guideline on how to mark methods and attributes as deprecated, and how we can schedule those for future removal, that would be awesome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kristjanvalur see my comment bellow about how to mark function as deprecated. for attributes, you can't use the decorator, so you just need to import warnings and warn deprecated if the user try to use this attribute, like we did here.
if not hasattr(self, "connection"): | ||
return | ||
return self.reset() | ||
async def close(self) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. This never should have returned Awaitable[NoReturn].
For the curious readers see this. Note that only a function that unconditionally raises an exception should do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and the pattern of returning awaitables through, rather than have an intermediate "await" is not a nice one and only serves to ofuscate. I guess people think they are optimizing (removing one 'await' level) but the effect is virtually unmeaasurable.
"""Alias for aclose(), for backwards compatibility""" | ||
await self.aclose() | ||
|
||
async def reset(self) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO the None in implied. It's a nit, do you feel differently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, depending on mypy warning levels, a return type is required. I have grown into the habit of always providing one explicitly.
redis/asyncio/client.py
Outdated
await self.aclose() | ||
|
||
async def reset(self) -> None: | ||
"""alias for aclose(), for backwards compatibility""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""Alias for aclose(), for backwards compatibility"""
assert p.subscribed is True | ||
assert p.subscribed is False | ||
|
||
async def test_close_is_aclose(self, r: redis.Redis): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome, thank you ❤️
await p.close() | ||
assert p.subscribed is False | ||
|
||
async def test_reset_is_aclose(self, r: redis.Redis): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and again
@dvora-h WDYT? Want to take it from here? Possibly a #breakingchange |
Sure, but what is the proper way do do that? |
7fd7e2d
to
c0a367f
Compare
@kristjanvalur Yes, we have a deprecated_function decorator. you can add it to the function. |
8a36bff
to
79de021
Compare
Codecov ReportPatch coverage:
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #2898 +/- ##
==========================================
- Coverage 91.38% 91.36% -0.02%
==========================================
Files 126 126
Lines 32469 32593 +124
==========================================
+ Hits 29671 29780 +109
- Misses 2798 2813 +15
☔ View full report in Codecov by Sentry. |
e3755b4
to
d31e501
Compare
@kristjanvalur conflicts... 🙂 |
d31e501
to
e02970f
Compare
@kristjanvalur The CI failed |
Bummer. Will investigate in a bit |
Oops, looks like I pushed the wrong branch today! Fixing. |
close() and reset() retained as aliases
e02970f
to
09076bc
Compare
Pull Request check-list
Please make sure to review and check all of these items:
$ tox
pass with this change (including linting)?NOTE: these things are not required to open a PR and can be done
afterwards / while the PR is open.
Description of change
The canonical name for an asynchronous
close()
method in async python isaclose()
. Providing this method allows support for standard programming patterns, including the use of theaclosing
context manager. Conversely, aclose()
method should be synchronous, for the same reason.This PR:
close()
toaclose()
method to several async classes, keeping the oldclose()
for backwards compatibility.reset()
toaclose()
, keepingclose()
andreset()
for compatibility in theasyncio.PubSub
class`.aclose()
method to the asyncPipeline
class, and aclose()
method to thePipeline
class.aclose()
method to the asyncConnectionPool
class and theclose()
method to the syncConnectionPool
.Having
close()
/aclose()
allows the use of theclosing
/aclosing
context managers. In particular, they can now be used to manage aConnectionPool
instance.Note: We keep the old asynchronous
close()
methods as alternatives, for backwards compatibility. Maybe we should actively deprecate them?