|
| 1 | +"""This script is NOT designed to be run as part of a pytest suite; it demands a clean |
| 2 | +Python process because it needs to be run before the shiny module is loaded. |
| 3 | +
|
| 4 | +The purpose of this script is to ensure that no shiny module initialization code uses |
| 5 | +the event loop. The running event loop during shiny module startup is different than the |
| 6 | +running event loop during the app's operation (I assume uvicorn creates the latter). The |
| 7 | +creation of, say, an asyncio.Lock during the course of shiny module startup will result |
| 8 | +in race conditions if the lock is used during the app's operation.""" |
| 9 | + |
| 10 | +if __name__ == "__main__": |
| 11 | + import sys |
| 12 | + import asyncio |
| 13 | + import importlib |
| 14 | + from typing import Optional |
| 15 | + |
| 16 | + if "shiny" in sys.modules: |
| 17 | + raise RuntimeError( |
| 18 | + "Bad test: shiny was already loaded, it's important that SpyEventLoopPolicy" |
| 19 | + " is installed before shiny loads" |
| 20 | + ) |
| 21 | + |
| 22 | + class SpyEventLoopPolicy(asyncio.DefaultEventLoopPolicy): |
| 23 | + def get_event_loop(self): |
| 24 | + raise RuntimeError("get_event_loop called") |
| 25 | + |
| 26 | + def set_event_loop(self, loop: Optional[asyncio.AbstractEventLoop]): |
| 27 | + raise RuntimeError("set_event_loop called") |
| 28 | + |
| 29 | + def new_event_loop(self): |
| 30 | + raise RuntimeError("new_event_loop called") |
| 31 | + |
| 32 | + asyncio.set_event_loop_policy(SpyEventLoopPolicy()) |
| 33 | + |
| 34 | + # Doing this instead of "import shiny" so no linter is tempted to remove it |
| 35 | + importlib.import_module("shiny") |
| 36 | + sys.stderr.write( |
| 37 | + "Success; shiny module loading did not attempt to access an asyncio event " |
| 38 | + "loop\n" |
| 39 | + ) |
0 commit comments