-
Notifications
You must be signed in to change notification settings - Fork 22
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
Johny b/561 non context manager #600
Changes from 8 commits
a2c8596
c783a7a
aac1f06
6ab76ee
43f101d
d7dc809
dd56281
475830c
0829514
801df6c
9f46b42
c4f7b5c
99d6142
2ae4515
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,24 @@ class Golem(_Engine): | |
at any given time. | ||
""" | ||
|
||
async def start(self) -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. weren't these methods supposed to be implemented on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
"""Initialize resources and start background services used by this engine. | ||
|
||
Calling this method is not necessary, it will be called either way internally | ||
when the engine is used for the first time. | ||
""" | ||
if not self.operative: | ||
await self._start() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've noticed that there's a difference between: async with Golem(...) as golem:
do_stuff_with(golem) and golem = Golem(...)
await golem.start()
do_stuff_with(golem)
await golem.stop() in case an exception is raised in With context manager, With the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that if the developer has any code that ensures E.g. our "recommended" way of doing things without context manager would be
(I skipped 'start` because it is optional and I don't think we need it in our examples) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @johny-b As I said, that's fine for me. But let's remember about this when creating documentation for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'll write the docs the way asyncio.Lock is documented, so |
||
|
||
async def stop(self) -> Optional[bool]: | ||
"""Stop the engine in a graceful way. | ||
|
||
This **must** be called when using Golem in a non-contextmanager way. | ||
""" | ||
if self.operative: | ||
return await self._stop(None, None, None) | ||
return None | ||
|
||
async def execute_tasks( | ||
self, | ||
worker: Callable[ | ||
|
@@ -111,6 +129,8 @@ async def worker(context: WorkContext, tasks: AsyncIterable[Task]): | |
print(completed.result.stdout) | ||
``` | ||
""" | ||
if not self.operative: | ||
await self.start() | ||
|
||
kwargs: Dict[str, Any] = {"payload": payload} | ||
if max_workers: | ||
|
@@ -203,6 +223,9 @@ async def main(): | |
await asyncio.sleep(REFRESH_INTERVAL_SEC) | ||
``` | ||
""" | ||
if not self.operative: | ||
await self.start() | ||
|
||
payload = payload or await service_class.get_payload() | ||
|
||
if not payload: | ||
|
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.
Shouldn't we set this to
True
at the end of the method? If any error occurs before the end of initialization we may end up with aGolem
instance that isoperative
but not properly initialized.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.
My reasoning was that we have to guard against more than one
start
running at the same time, as in:(I don't think anyone will write exactly this but this might somehow happen in a more complex code, I guess?).
We could store two bits of information,
start_started
,start_ended
.But maybe we should rename
operative
? It is now if factstarted_not_stopped
. Or maybe just remove a public property?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.
@johny-b You've raised a good point. Consider again your example:
Suppose
golem.start()
in line#1
is in progress andgolem.start()
in#2
starts. We don't want the second instance ofgolem._start()
to run concurrently with the first one, and your current solution will prevent that. But we also don't wantgolem.start()
in line#2
exiting immediately, as ifgolem
were already properly started, while_start()
is still in progress! This is even more important when we replace bothgolem.start()
's withgolem.execute_tasks()
.What we do want is for the second
golem.start()
to wait until the first one finishes. So we need some kind of task synchronisation mechanism, e.g. anasyncio.Lock
.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.
That's a really good point. I need to think about this : )
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.
Fixed in 0829514
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.
@johny-b Thanks. Now it probably works well, but the solution using
asyncio.Futures
is a bit hard to follow, at least for me.Why not something more straightforward, with
asyncio.Lock
:EDIT: Perhaps
stop()
should differ fromstart()
in thatself._stopped = True
goes beforeawait self.stop()
. And there's the complication with theexc_info
tuple being passed around from__aexit__()
.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.
Additionally, if you put
start()
andstop()
in_Engine
instead ofGolem
, you could define_Engine
's__aenter__()
and__aexit__()
in terms ofstart()
andstop()
. That would also protect us from concurrent executions ofasync with golem:...
in two different tasks.