Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Flask contexts not available inside background callback #2235

Closed
ArtsiomAntropau opened this issue Sep 17, 2022 · 8 comments · Fixed by #3051
Closed

Flask contexts not available inside background callback #2235

ArtsiomAntropau opened this issue Sep 17, 2022 · 8 comments · Fixed by #3051
Labels
bug something broken P3 backlog

Comments

@ArtsiomAntropau
Copy link

Describe your context

dash                              2.6.1
dash-core-components              2.0.0
dash-html-components              2.0.0
dash-table                        5.0.0

Describe the bug

Background callbacks don't have flask's app and request contexts inside.

Expected behavior

Background callbacks have flask's app and request contexts inside.

So, after creating callback function and before providing them to the Celery, we should provide flask contexts inside this function to imitate the default callback behaviour

with flask_app.app_context():
    return (copy_current_request_context(job_fn) if has_request_context() else job_fn)(*args, **kwargs)

Any recommendations for now?

@ArtsiomAntropau ArtsiomAntropau changed the title [BUG] [BUG] Flask contexts not available inside background callback Sep 17, 2022
@T4rk1n
Copy link
Contributor

T4rk1n commented Sep 20, 2022

For celery the copy_current_request_context method doesn't work. An alternative we could do is provide the values like headers, path, remote, etc. inside the callback_context instead.

@ArtsiomAntropau
Copy link
Author

@T4rk1n can you highlight why it doesn't work? In common, this function simply copies the current request context and provided it inside the function, like dash already doing with dash callback context.

@ArtsiomAntropau
Copy link
Author

ArtsiomAntropau commented Sep 20, 2022

The main problem and thing that we need to remember – we should not try to push it when we're packing the function for celery, it should be passed to the prepared function as arguments (I mean wrapper that will call user's callback function).

@T4rk1n
Copy link
Contributor

T4rk1n commented Sep 21, 2022

The issue is that wrapping the function in the callback where it should be, doesn't return a celery task object to call (Missing func.delay). The callback_context I refactored to use a context_var instead of the flask.g object for that matter.

@ArtsiomAntropau
Copy link
Author

@T4rk1n so what do you think about it? (except of temp inability to serialize contextvars)

def flask_patcher():
    with flask.current_app.app_context():
        @flask.copy_current_request_context
        def _(fn, *args, **kwargs):
            return fn(*args, **kwargs)

    return _
    def call_job_fn(self, key, job_fn, args, context):
        task = job_fn.delay(key, self._make_progress_key(key), args, flask_patcher())
        return task.task_id
    @celery_app.task(name=f"long_callback_{fn_hash}")
    def job_fn(result_key, progress_key, user_callback_args, context=None, patcher=None):
                if isinstance(user_callback_args, dict):
                    user_callback_output = patcher(fn, *maybe_progress, **user_callback_args)
                elif isinstance(user_callback_args, (list, tuple)):
                    user_callback_output = patcher(fn, *maybe_progress, *user_callback_args)
                else:
                    user_callback_output = patcher(fn, *maybe_progress, user_callback_args)

@T4rk1n
Copy link
Contributor

T4rk1n commented Sep 21, 2022

@ArtsiomAntropau If you can prove that works with a test, (can add path=flask.request.path to tests/integration/long_callback/app_callback_ctx.py line 34 then test in test_lcbc012_long_callback_ctx) I'll gladly take a look in a PR.

@alexcjohnson
Copy link
Collaborator

From @JamesKunstle in #2636:

Update: For anyone looking for a solution, you can pass the 'request=flask.request' object into background callbacks and that seems to work.

https://community.plotly.com/t/how-to-read-cookies-inside-a-background-callback/70224/11

@kovalev-vladimir-da
Copy link

Hi @alexcjohnson !
It appears for me (and not only) that this method is not working cus i gor "Working outside of request context"

So, this solution request=flask.request is working when DiskCache is on, but not for Celery.

@gvwilson gvwilson self-assigned this Jul 25, 2024
@gvwilson gvwilson removed their assignment Aug 2, 2024
@gvwilson gvwilson added P3 backlog bug something broken labels Aug 13, 2024
@gvwilson gvwilson changed the title [BUG] Flask contexts not available inside background callback Flask contexts not available inside background callback Aug 13, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug something broken P3 backlog
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants