Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use django 3.0 ORM in a Jupyter Notebook without triggering the async context check?

Django 3.0 is adding asgi / async support and with it a guard around making synchronous requests in an async context. Concurrently, IPython just added top level async/await support, which seems to be running the whole interpreter session inside of a default event loop.

Unfortunately the combination of these two great addition means that any django ORM operation in a jupyter notebook causes a SynchronousOnlyOperation exception:

SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.

As the exception message says, it's possible to wrap each ORM call in a sync_to_async() like:

images = await sync_to_async(Image.objects.all)()

but it's not very convenient, especially for related fields which would usually be implicitly resolved on attribute lookup.

(I tried %autoawait off magic but it didn't work, from a quick glance at the docs I'm assuming it's because ipykernels always run in an asyncio loop)

So is there a way to either disable the sync in async context check in django or run an ipykernel in a synchronous context?


For context: I wrote a data science package that uses django as a backend server but also exposes a jupyter based interface on top of the ORM that allows you to clean/annotate data, track machine learning experiments and run training jobs all in a jupyter notebook.

like image 713
michalwols Avatar asked Nov 30 '19 19:11

michalwols


People also ask

Does Django ORM support async?

Django has support for writing asynchronous (“async”) views, along with an entirely async-enabled request stack if you are running under ASGI. Async views will still work under WSGI, but with performance penalties, and without the ability to have efficient long-running requests.

Can Django be used in Jupyter notebook?

Django for Jupyter. It's true packages exist to make it "easy" to use Django inside of a jupyter notebook. I seem to always run into issues successfully running these packages. I've found the below method useful although I cannot recall how I discovered how this works (aka attribution needed).

Is Django asynchronous?

When writing an application, asynchronous code allows you to speed up an application that has to deal with a high number of tasks simultaneously. With the Django 3.1 release, Django now supports async views, so if you are running ASGI, writing async specific tasks is now possible!

What is Async_to_sync?

async_to_sync turns an awaitable into a synchronous callable, and asyncio. run executes a coroutine and return the result. According to documentation, a callable from async_to_sync works in a subthread.


3 Answers

It works for me

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

BTW, I start my notebook using the command

./manage.py shell_plus --notebook
like image 175
Wojtas Koziej Avatar answered Oct 23 '22 16:10

Wojtas Koziej


Contrary to other answers I'd suggest just running from the shell as:

env DJANGO_ALLOW_ASYNC_UNSAFE=true ./manage.py shell_plus --notebook

and not modifying any config files or startup scripts.

The advantage of doing it like this is that these checks still seem useful to have enabled almost everywhere else (e.g. when debugging locally via runserver or when running tests). Disabling via files would easily disable these in too many places negating their advantage.

Note that most shells provide easy ways of recalling previously invoked command lines, e.g. in Bash or Zsh Ctrl+R followed by notebook would find the last time you ran something that had "notebook" in. Under the fish shell just type notebook and press the up arrow key to start a reverse search.

like image 41
Sam Mason Avatar answered Oct 23 '22 18:10

Sam Mason


For now I plan on just using a forked version of django with a new setting to skip the async_unsafe check. Once the ORM gets async support I'll probably have to rewrite my project to support it and drop the flag.

EDIT: there's now a PR to add an env variable (DJANGO_ALLOW_ASYNC_UNSAFE) to disable the check (https://github.com/django/django/pull/12172)

like image 5
michalwols Avatar answered Oct 23 '22 18:10

michalwols