Skip to content
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

callbacks: introduce scoped callbacks #1460

Merged
merged 11 commits into from
Jan 10, 2024

Conversation

skshetry
Copy link
Contributor

  1. This PR introduces a close() API that gives an opportunity for callbacks to close themselves, either on error or on completions. We use this to extend Callbacks into a context-manager by using that close() api on __exit__().

  2. This PR also introduces branched() API that returns an instance of a callback. Since the callback is a context-manager, this gives an opportunity to close child callbacks after they complete. This is very similar to branch() API, but unlike branch, it should not mutate passed kwargs, and always return an instance of a callback. The default return is _DEFAULT_CALLBACK or NoopCallback if left unimplemented.

    For backwards compatibility, this will still call branch() API, try to read from the kwargs it has set, pop the callback kwarg and return it. But anyone implementing Callback does not need to implement branch API and should prefer branched() instead from now on, and the older API should be considered deprecated.

Closes #1075.

@skshetry skshetry force-pushed the scoped-callback branch 2 times, most recently from 85a0575 to b187043 Compare December 12, 2023 07:29
@skshetry skshetry marked this pull request as ready for review December 12, 2023 14:59
1. This PR introduces a `close()` API that gives an opportunity
   for callbacks to close themselves, either on error or on completions.
   We use this to extend `Callback`s into a context-manager by using that
   `close()` api on `__exit__()`.
2. This PR also introduces `branched()` API that returns an instance of
   a callback. Since the callback is a context-manager, this gives an
   opportunity to close child callbacks after they complete.
   This is very similar to `branch()` API, but unlike `branch`, it should
   not mutate passed `kwargs`, and always return an instance of a callback.
   The default return is _DEFAULT_CALLBACK or NoopCallback if left unimplemented.

   For backwards compatibility, this will still call `branch()` API, try to read from
   the kwargs it has set, pop the `callback` kwarg and return it.
   But anyone implementing `Callback` does not need to implement `branch` API and should
   prefer `branched()` instead from now on, and the older API should be considered deprecated.

Closes fsspec#1075.
@skshetry
Copy link
Contributor Author

skshetry commented Jan 9, 2024

ping @martindurant

@martindurant
Copy link
Member

Could we please have some tests that call the new API? I see you made some changes in the batch operations, and the existing tests pass, so that's good.

@@ -568,8 +568,8 @@ async def _put(
coros = []
callback.set_size(len(file_pairs))
for lfile, rfile in file_pairs:
callback.branch(lfile, rfile, kwargs)
coros.append(self._put_file(lfile, rfile, **kwargs))
put_file = callback.branch_coro(self._put_file)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like adding branch_coro but I am not sure how to support scoped/structured callbacks in async without one either, since we are working task/futures.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is not well explorer territory. I would have thought that all calls into the callbacks machinery can by sync, since they will be fast and so safe to call from sync or async contexts.

@skshetry
Copy link
Contributor Author

skshetry commented Jan 9, 2024

Could we please have some tests that call the new API? I see you made some changes in the batch operations, and the existing tests pass, so that's good.

I have added the tests.

@martindurant
Copy link
Member

@efiop , any thoughts on this?

Copy link
Member

@efiop efiop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

def close(self):
"""Close callback."""

def branched(self, path_1, path_2, kwargs):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, I matched the function signature with branch, but I am open to any suggestions here (if there should be kwargs or **kwargs or if it should be there at all).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no better suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to think of what could be the use case of kwargs. So far, it feels like an unnecessary required argument to pass.

I think following will be an example of how someone will use a branched callback.

with callback.branched(path_1, path_2, {}) as child:
    pass

The kwargs feel unnecessary to me. Either it should be kwargs=None by default or should be removed. I am leaning towards removing it though (can be added if we get some requests).

@martindurant martindurant merged commit 24060cc into fsspec:master Jan 10, 2024
10 checks passed
@skshetry skshetry deleted the scoped-callback branch January 10, 2024 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feature: scoped callbacks
3 participants