1

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?

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. Commented Jan 12 at 19:21

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Despite your caveat, your answer worked. After adding get_asyncio_loop().run_until_complete( dbinit() ) to my startup file, I can now run queries as soon as the shell opens.
BTW - I'm not familiar with iPython cells, but I'll add it to my study queue.
0

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())

7 Comments

To clarify — I can't run this code in a startup file because await can't be used there, correct?
Yes, I completely missed the mark here. What about using the run_cell hook? I'll edit my answer with that approach.
I appreciate your persistence and helpfulness. Unfortunately, the run_cell solution didn't work, by which I mean the lambda function was never called. I was curious about the get_ipython() and events.register() functions, so I looked them up and set up tests for all five even types, but none of them ever fired. (ipython.readthedocs.io/en/stable/config/callbacks.html#id1)
Well, shoot. Hopefully someone will stop by with persistence and more helpful helpfulness.
Addendum: After the shell opens, when I enter a statement and press return, at that point four of the five events fire and call my functions - all but shell_initialized. I don't know why that one never gets called. Anyway, it seems these events run on every line entered, which isn't quite the right fit for this situation. -- At least I learned something.
|
0

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()
            """)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.