|
| 1 | +from asyncio import gather, run, Semaphore |
| 2 | +from dominate.dom_tag import async_context_id |
| 3 | +from textwrap import dedent |
| 4 | + |
| 5 | +from dominate import tags |
| 6 | + |
| 7 | +# To simulate sleep without making the tests take a hella long time to complete |
| 8 | +# lets use a pair of semaphores to explicitly control when our coroutines run. |
| 9 | +# The order of execution will be marked as comments below: |
| 10 | +def test_async_bleed(): |
| 11 | + async def tag_routine_1(sem_1, sem_2): |
| 12 | + root = tags.div(id = 1) # [1] |
| 13 | + with root: # [2] |
| 14 | + sem_2.release() # [3] |
| 15 | + await sem_1.acquire() # [4] |
| 16 | + tags.div(id = 2) # [11] |
| 17 | + return str(root) # [12] |
| 18 | + |
| 19 | + async def tag_routine_2(sem_1, sem_2): |
| 20 | + await sem_2.acquire() # [5] |
| 21 | + root = tags.div(id = 3) # [6] |
| 22 | + with root: # [7] |
| 23 | + tags.div(id = 4) # [8] |
| 24 | + sem_1.release() # [9] |
| 25 | + return str(root) # [10] |
| 26 | + |
| 27 | + async def merge(): |
| 28 | + sem_1 = Semaphore(0) |
| 29 | + sem_2 = Semaphore(0) |
| 30 | + return await gather( |
| 31 | + tag_routine_1(sem_1, sem_2), |
| 32 | + tag_routine_2(sem_1, sem_2) |
| 33 | + ) |
| 34 | + |
| 35 | + # Set this test up for failure - pre-set the context to a non-None value. |
| 36 | + # As it is already set, _get_async_context_id will not set it to a new, unique value |
| 37 | + # and thus we won't be able to differentiate between the two contexts. This essentially simulates |
| 38 | + # the behavior before our async fix was implemented (the bleed): |
| 39 | + async_context_id.set(0) |
| 40 | + tag_1, tag_2 = run(merge()) |
| 41 | + |
| 42 | + # This looks wrong - but its what we would expect if we don't |
| 43 | + # properly handle async... |
| 44 | + assert tag_1 == dedent("""\ |
| 45 | + <div id="1"> |
| 46 | + <div id="3"> |
| 47 | + <div id="4"></div> |
| 48 | + </div> |
| 49 | + <div id="2"></div> |
| 50 | + </div> |
| 51 | + """).strip() |
| 52 | + |
| 53 | + assert tag_2 == dedent("""\ |
| 54 | + <div id="3"> |
| 55 | + <div id="4"></div> |
| 56 | + </div> |
| 57 | + """).strip() |
| 58 | + |
| 59 | + # Okay, now lets do it right - lets clear the context. Now when each async function |
| 60 | + # calls _get_async_context_id, it will get a unique value and we can differentiate. |
| 61 | + async_context_id.set(None) |
| 62 | + tag_1, tag_2 = run(merge()) |
| 63 | + |
| 64 | + # Ah, much better... |
| 65 | + assert tag_1 == dedent("""\ |
| 66 | + <div id="1"> |
| 67 | + <div id="2"></div> |
| 68 | + </div> |
| 69 | + """).strip() |
| 70 | + |
| 71 | + assert tag_2 == dedent("""\ |
| 72 | + <div id="3"> |
| 73 | + <div id="4"></div> |
| 74 | + </div> |
| 75 | + """).strip() |
0 commit comments