Calling asyncio.get_event_loop() from outside a coroutine, with no running loop and no loop set on the current thread, emitted a DeprecationWarning in Python 3.10 and 3.12 and is on a path to raising RuntimeError. The historical behaviour of quietly creating a new loop is what made it dangerous: a library that called it at import time got a loop nobody would ever run, and awaits scheduled on it never executed.
The replacements are precise about intent. asyncio.get_running_loop() inside a coroutine returns the loop that is running and raises RuntimeError if there is none. asyncio.run(main()) at the top level creates a loop, runs the coroutine, cancels remaining tasks and closes the loop.
The common breakage is a class that captures a loop in __init__ for later use. Stop doing that: fetch the loop where it is needed with get_running_loop(), or take the loop as an argument. Objects such as asyncio.Queue and asyncio.Lock no longer bind a loop at construction in modern versions, so constructing them outside a coroutine is fine again.