Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async get_default_backend doesn't check to see if the current I/O library has changed #991

Closed
tjstum opened this issue Sep 20, 2023 · 3 comments

Comments

@tjstum
Copy link

tjstum commented Sep 20, 2023

Describe the bug
If you have a program that uses asyncio and trio (which can happen when using trio-asyncio), calls made from a differently-flavored async function cause a failure (from using the wrong backend).

If you'd like, I can submit a fix, but I figured I would file this first, in case you have a specific way you want it handled.

To Reproduce
If starting with asyncio, then switching to trio:

>>> import dns.asyncresolver
>>> import asyncio, trio
>>> asyncio.run(dns.asyncresolver.resolve("google.com"))
<dns.resolver.Answer object at 0x7f3187cba860>
>>> trio.run(dns.asyncresolver.resolve, "google.com")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/trio/_core/_run.py", line 2093, in run
    raise runner.main_task_outcome.error
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/dns/asyncresolver.py", line 303, in resolve
    return await get_default_resolver().resolve(
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/dns/asyncresolver.py", line 88, in resolve
    await backend.sleep(backoff)
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/dns/_asyncio_backend.py", line 266, in sleep
    await asyncio.sleep(interval)
  File "/opt/hrt/hrtpython-3.10/lib/python3.10/asyncio/tasks.py", line 599, in sleep
    loop = events.get_running_loop()
RuntimeError: no running event loop

Or, if starting with trio, then switching to asyncio:

>>> import dns.asyncresolver
>>> import asyncio, trio
>>> trio.run(dns.asyncresolver.resolve, "google.com")
<dns.resolver.Answer object at 0x7f227b88fd30>
>>> asyncio.run(dns.asyncresolver.resolve("google.com"))
Traceback (most recent call last):
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/trio/_core/_generated_run.py", line 54, in current_time
    return GLOBAL_RUN_CONTEXT.runner.current_time()
AttributeError: 'RunContext' object has no attribute 'runner'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/hrt/hrtpython-3.10/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/opt/hrt/hrtpython-3.10/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/dns/asyncresolver.py", line 303, in resolve
    return await get_default_resolver().resolve(
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/dns/asyncresolver.py", line 88, in resolve
    await backend.sleep(backoff)
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/dns/_trio_backend.py", line 236, in sleep
    await trio.sleep(interval)
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/trio/_timeouts.py", line 87, in sleep
    await sleep_until(trio.current_time() + seconds)
  File "/usr/local/venvs/opsvenv2/lib/python3.10/site-packages/trio/_core/_generated_run.py", line 56, in current_time
    raise RuntimeError("must be called from async context")
RuntimeError: must be called from async context

Context (please complete the following information):

  • dnspython version [e.g. 2.2.1] 2.4.1
  • Python version [e.g. 3.10.0] 3.10.13
  • OS: [e.g. macOS Monterey] Debian Linux
@rthalley
Copy link
Owner

Hmm, I'm not sure if get_default_backend() should have to probe every time because someone might change the backend. This issue can be worked around by calling set_default_backend() like the tests do, or by specifying a backend to async I/O routines if you really want to mix trio and asyncio. Do you think that's enough?

@tjstum
Copy link
Author

tjstum commented Sep 25, 2023

For what it's worth, on my host, sniff() appears to be about 400 nanoseconds, which for most of our microbenchmarks, is below the level of which we'd worry about. Since get_backend itself has caching, the overall performance penalty here seems very minor, compared to the obscurity of this error.

But it's up to you, of course! I have a workaround in my application; always passing in backend=get_backend(sniff()) to resolve.

@rthalley
Copy link
Owner

I've decided to leave things as-is for now, but if this becomes a more frequent problem I will reconsider.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants