Skip to content

Sync/Async issues when integrating with Django 3.1.5 #439

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

Closed
caramdache opened this issue Jan 19, 2021 · 5 comments
Closed

Sync/Async issues when integrating with Django 3.1.5 #439

caramdache opened this issue Jan 19, 2021 · 5 comments

Comments

@caramdache
Copy link

I'm currently converting some puppeteer code to playright-python. And I'm taking this opportunity to use my django models rather than raw SQL queries:

#!/usr/bin/env python3

import os, sys
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django.setup()

from playwright import sync_playwright

from models import SomeModelClass

with sync_playwright() as playwright:
    objs = SomeModelClass.objects.filter(pk=1)
    print(objs)

However, I'm running into some issues and I would appreciate some guidance:

  1. I started by using the sync API, but when I try to run a query like: SomeModelClass.objects.get(pk=1), I get the following error: django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
  2. I then tried to use sync_to_async, from https://docs.djangoproject.com/en/3.1/topics/async/#asgiref.sync.sync_to_async. But this leads to further errors.
  3. I then converted by entire code to use the async API, but this did not quite solve the issue, since django querysets are evaluated lazyly, i.e. outside the (in this case) async method.
  4. I did build upon 3. and forced the queryset to be evaluated inside the async method:
@sync_to_async
def getData():
    objs= SomeModelClass.objects.filter(pk=1)
    return list(objs)

This seemed to work, but at the expense of code complexity and I starting running into timeout issues.

  1. The only solution that's been easy to use was as follows:
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"

But, as pointed out by django, it may result in data loss.

Any advice from the team?

@kumaraditya303
Copy link
Contributor

I'm currently converting some puppeteer code to playright-python. And I'm taking this opportunity to use my django models rather than raw SQL queries:

#!/usr/bin/env python3

import os, sys
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django.setup()

from playwright import sync_playwright

from models import SomeModelClass

with sync_playwright() as playwright:
    objs = SomeModelClass.objects.filter(pk=1)
    print(objs)

However, I'm running into some issues and I would appreciate some guidance:

  1. I started by using the sync API, but when I try to run a query like: SomeModelClass.objects.get(pk=1), I get the following error: django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
  2. I then tried to use sync_to_async, from https://docs.djangoproject.com/en/3.1/topics/async/#asgiref.sync.sync_to_async. But this leads to further errors.
  3. I then converted by entire code to use the async API, but this did not quite solve the issue, since django querysets are evaluated lazyly, i.e. outside the (in this case) async method.
  4. I did build upon 3. and forced the queryset to be evaluated inside the async method:
@sync_to_async
def getData():
    objs= SomeModelClass.objects.filter(pk=1)
    return list(objs)

This seemed to work, but at the expense of code complexity and I starting running into timeout issues.

  1. The only solution that's been easy to use was as follows:
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"

But, as pointed out by django, it may result in data loss.

Any advice from the team?

The documentation is pointing about not using it in the application, not in testing, in testing it is fine also the env var should be configured in a conftest.py file to separate it from the real application. Ref

@caramdache
Copy link
Author

os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"

But, as pointed out by django, it may result in data loss.
Any advice from the team?

The documentation is pointing about not using it in the application, not in testing, in testing it is fine also the env var should be configured in a conftest.py file to separate it from the real application. Ref

But I am using it in an application, not in testing. So this is why using DJANGO_ALLOW_ASYNC_UNSAFE is not such a good idea. Sorry if that wasn't clear from my original post.

@pavelfeldman pavelfeldman self-assigned this Jan 20, 2021
@pavelfeldman
Copy link
Member

@caramdache thanks for the report.

It looks like the SQL business is annotated with the async_unsafe decoration implemented here. Every call annotated as such will check if it is running inside event loop.

Playwright's sync API uses asyncio loop internally, it uses greenlets to simulate blocking synchronous behavior, but still runs the event loop inside. So django is getting confused. I'll see if we can do something about it. But the nature of the check suggests that it is a development-time assertion. If you know that you only hit this from within Playwright, it is probably fine to disable the check. Having said that, if you disable it and bring in more code into your project you might miss a real offender.

@mxschmitt
Copy link
Member

Closing for now since the workaround seems working.

jedie added a commit to boxine/bx_django_utils that referenced this issue Sep 21, 2022
Clarify why we set "DJANGO_ALLOW_ASYNC_UNSAFE", see: microsoft/playwright-python#439 (comment)
jedie added a commit to boxine/bx_django_utils that referenced this issue Sep 22, 2022
Clarify why we set "DJANGO_ALLOW_ASYNC_UNSAFE", see:
microsoft/playwright-python#439 (comment)
@Wedge009
Copy link

The only solution that's been easy to use was as follows:
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
Any advice from the team?

Closing for now since the workaround seems working.

Just wondering if this is still the official advice. I've just started trying to use Playwright (completely new to it) in a Django 5 project and encountered the SynchronousOnlyOperation exception. Disabling this check seemed to help but it wasn't till I read this issue that I understood why it was 'okay' to do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants