admin管理员组

文章数量:1134247

I have set IPYTHONDIR=.ipython, and created a startup file at .ipython/profile_default/startup/01_hello.py. Now, when I run ipython, it executes the contents of that file as if they had been entered into the IPython shell.

I can run sync code this way:

# contents of 01_hello.py
print( "hello!" )
$ ipython
Python 3.12.0 (main, Nov 12 2023, 10:40:37) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.
hello

In [1]: 

I can also run async code directly in the shell:

# contents of 01_hello.py
print( "hello!" )
async def foo():
    print( "foo" )
$ ipython
Python 3.12.0 (main, Nov 12 2023, 10:40:37) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.
hello

In [1]: await foo()
foo

In [2]: 

However, I cannot run async code in the startup file, even though it's supposed to be as if that code was entered into the shell:

# contents of 01_hello.py
print( "hello!" )
async def foo():
    print( "foo" )
await foo()
$ ipython
Python 3.12.0 (main, Nov 12 2023, 10:40:37) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.
[TerminalIPythonApp] WARNING | Unknown error in handling startup files:
  File ~/proj/.ipython/profile_default/startup/01_imports.py:5
    await foo()
    ^
SyntaxError: 'await' outside function

Question: Why doesn't this work, and is there a way to run async code in the startup file without explicitly starting a new event loop just for that? (asyncio.run())

Doing that wouldn't make sense, since that event loop would have to close by the end of the file, which makes it impossible to do any initialization work that involves context vars (which is where Tortoise-ORM stores its connections), which defeats the purpose.

Or stated differently: How can I access the event loop that IPython starts for the benefit of the interactive shell?

I have set IPYTHONDIR=.ipython, and created a startup file at .ipython/profile_default/startup/01_hello.py. Now, when I run ipython, it executes the contents of that file as if they had been entered into the IPython shell.

I can run sync code this way:

# contents of 01_hello.py
print( "hello!" )
$ ipython
Python 3.12.0 (main, Nov 12 2023, 10:40:37) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.
hello

In [1]: 

I can also run async code directly in the shell:

# contents of 01_hello.py
print( "hello!" )
async def foo():
    print( "foo" )
$ ipython
Python 3.12.0 (main, Nov 12 2023, 10:40:37) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.
hello

In [1]: await foo()
foo

In [2]: 

However, I cannot run async code in the startup file, even though it's supposed to be as if that code was entered into the shell:

# contents of 01_hello.py
print( "hello!" )
async def foo():
    print( "foo" )
await foo()
$ ipython
Python 3.12.0 (main, Nov 12 2023, 10:40:37) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.
[TerminalIPythonApp] WARNING | Unknown error in handling startup files:
  File ~/proj/.ipython/profile_default/startup/01_imports.py:5
    await foo()
    ^
SyntaxError: 'await' outside function

Question: Why doesn't this work, and is there a way to run async code in the startup file without explicitly starting a new event loop just for that? (asyncio.run())

Doing that wouldn't make sense, since that event loop would have to close by the end of the file, which makes it impossible to do any initialization work that involves context vars (which is where Tortoise-ORM stores its connections), which defeats the purpose.

Or stated differently: How can I access the event loop that IPython starts for the benefit of the interactive shell?

Share Improve this question asked Jan 7 at 18:52 odigityodigity 8,0565 gold badges41 silver badges57 bronze badges 1
  • FYI - Until now, I've only been using iPython as a fancier shell alternative to the default REPL. I have not delved into the land of notebooks and cells and all that other stuff. I guess I'll have to, now, to understand some of these answers. – odigity Commented 19 hours ago
Add a comment  | 

3 Answers 3

Reset to default 1

From version 8, ipython uses a function called get_asyncio_loop to get access to the event loop that it runs async cells on. You can use this event loop during your startup script to run any tasks you want on the same event loop that async cells will run on.

NB. This is only uses for the asyncio package in Python's standard library and not any other async libraries (such as trio).

from IPython.core.async_helpers import get_asyncio_loop as _get_asyncio_loop

async def foo():
    print("foo")

_get_asyncio_loop().run_until_complete(foo())

Caveat

The event loop that ipython uses DOES NOT run in the background. What this means is that unless you are running an async cell, no tasks that you have started will be running. ie. None of your Tortoise ORM connections will be serviced, which may cause them to break.

As such, you may need to run your Tortoise ORM in a separate event loop anyway, and write some glue for passing data back and forth between the two event loops.

Edit: I initially suggested using IPython's get_ipython function to access the IPython instance and start a new event loop, but that doesn't meet odigity's needs at all. Instead, IPython's run_cell hook could be used in the startup file:

import asyncio
from IPython import get_ipython

async def foo():
    print("foo")

# Define a function to run async code using IPython's event loop
def run_async_initialization():
    ipython = get_ipython()
    if ipython is not None:
        coro = foo()
        future = asyncio.ensure_future(coro)

# Register the function to run after the IPython shell initializes
ip = get_ipython()
if ip is not None:
    ip.events.register('post_run_cell', lambda: run_async_initialization())

You want to use IPython's autoawait feature, that is currently not applied to the startup scripts, only to cells.

You can run cells from within the startup script, so autoawait would work:

# contents of 01_hello.py
ip = get_ipython()
ip.run_cell("""
    async def foo():
        print( "foo" )
    await foo()
            """)

本文标签: pythonHow to run async code in IPython startup filesStack Overflow