|
| 1 | +import asyncio |
| 2 | +import time |
| 3 | + |
| 4 | +from jupyter_server.auth.utils import get_anonymous_username |
| 5 | +from jupyter_server.base.handlers import CURRENT_JUPYTER_HANDLER, JupyterHandler |
| 6 | +from jupyter_server.services.kernels.kernelmanager import AsyncMappingKernelManager |
| 7 | + |
| 8 | + |
| 9 | +async def test_jupyter_handler_contextvar(jp_fetch, monkeypatch): |
| 10 | + # Create some mock kernel Ids |
| 11 | + kernel1 = "x-x-x-x-x" |
| 12 | + kernel2 = "y-y-y-y-y" |
| 13 | + |
| 14 | + # We'll use this dictionary to track the current user within each request. |
| 15 | + context_tracker = { |
| 16 | + kernel1: {"started": "no user yet", "ended": "still no user", "user": None}, |
| 17 | + kernel2: {"started": "no user yet", "ended": "still no user", "user": None}, |
| 18 | + } |
| 19 | + |
| 20 | + # Monkeypatch the get_current_user method in Tornado's |
| 21 | + # request handler to return a random user name for |
| 22 | + # each request |
| 23 | + async def get_current_user(self): |
| 24 | + return get_anonymous_username() |
| 25 | + |
| 26 | + monkeypatch.setattr(JupyterHandler, "get_current_user", get_current_user) |
| 27 | + |
| 28 | + # Monkeypatch the kernel_model method to show that |
| 29 | + # the current context variable is truly local and |
| 30 | + # not contaminated by other asynchronous parallel requests. |
| 31 | + def kernel_model(self, kernel_id): |
| 32 | + # Get the Jupyter Handler from the current context. |
| 33 | + current: JupyterHandler = CURRENT_JUPYTER_HANDLER.get() |
| 34 | + # Get the current user |
| 35 | + context_tracker[kernel_id]["user"] = current.current_user |
| 36 | + context_tracker[kernel_id]["started"] = current.current_user |
| 37 | + time.sleep(2.0) |
| 38 | + # Track the current user a few seconds later. We'll |
| 39 | + # verify that this user was unaffected by other parallel |
| 40 | + # requests. |
| 41 | + context_tracker[kernel_id]["ended"] = current.current_user |
| 42 | + return {"id": kernel_id, "name": "blah"} |
| 43 | + |
| 44 | + monkeypatch.setattr(AsyncMappingKernelManager, "kernel_model", kernel_model) |
| 45 | + |
| 46 | + # Make two requests in parallel. |
| 47 | + await asyncio.gather(jp_fetch("api", "kernels", kernel1), jp_fetch("api", "kernels", kernel2)) |
| 48 | + |
| 49 | + # Assert that the two requests had different users |
| 50 | + assert context_tracker[kernel1]["user"] != context_tracker[kernel2]["user"] |
| 51 | + # Assert that the first request started+ended with the same user |
| 52 | + assert context_tracker[kernel1]["started"] == context_tracker[kernel1]["ended"] |
| 53 | + # Assert that the second request started+ended with the same user |
| 54 | + assert context_tracker[kernel2]["started"] == context_tracker[kernel2]["ended"] |
0 commit comments