watchfiles icon indicating copy to clipboard operation
watchfiles copied to clipboard

RuntimeError: Already borrowed since last release when

Open bellini666 opened this issue 1 year ago • 13 comments

Description

When exiting my application I'm getting RuntimeError: Already borrowed error

Example Code

No response

Watchfiles Output

future: <Task finished name='Task-9' coro=<Agent.process_door_events() done, defined at /home/bellini/dev/2u/nomad/nomad/agents/base.py:176> exception=RuntimeError('Already borrowed')>
Traceback (most recent call last):
  File "/home/bellini/dev/2u/nomad/nomad/agents/base.py", line 181, in process_door_events
    async for door_opened in self.listen_for_door_events():
  File "/home/bellini/dev/2u/nomad/nomad/agents/dev.py", line 66, in listen_for_door_events
    async for changes in watchfiles.awatch(self._incoming_file):
  File "/home/bellini/dev/2u/nomad/.venv/lib/python3.10/site-packages/watchfiles/main.py", line 222, in awatch
    with RustNotify([str(p) for p in paths], debug, force_polling, poll_delay_ms) as watcher:
RuntimeError: Already borrowed

Operating System & Architecture

Debian Sid amd64

Environment

python3.10

Python & Watchfiles Version

python: 3.10.5 (main, Jun 8 2022, 09:26:22) [GCC 11.3.0], watchfiles: 0.16.0

Rust & Cargo Version

cargo 1.56.0 rustc 1.59.0

bellini666 avatar Jul 25 '22 16:07 bellini666

Thanks for reporting.

To help narrow down the error, please can you check if you get the same error with v0.15?

samuelcolvin avatar Jul 25 '22 19:07 samuelcolvin

Also:

  • are you getting the error all the time, or intermittently?
  • Is there anything unusual about your setup or anything specific which is causing the error?

samuelcolvin avatar Jul 25 '22 19:07 samuelcolvin

To help narrow down the error, please can you check if you get the same error with v0.15?

I don't, the error started happening once I upgraded to v0.16 (sorry, I think I didn't make this clear enough =P)

are you getting the error all the time, or intermittently?

I only get the error when exiting my app. I stop it with SIGINT and I handle it myself, where I cancel the task that is running async for changed in watchfiles.awatch(...)

Is there anything unusual about your setup or anything specific which is causing the error?

Maybe the fact that I handle SIGNINT/KeyboardInterrupt to gracefully exit the application.

bellini666 avatar Jul 25 '22 20:07 bellini666

Ok, I don't get this and obviously the tests don't pick it up.

Can you share some example code that's causing the error?

samuelcolvin avatar Jul 25 '22 20:07 samuelcolvin

@samuelcolvin this example should reproduce the issue is you run it and either ctrl+c or kill -2 <PID> it and the error should happen.

I added some prints to show that the code follows the expected path and it finishes gracefully, but I can see the RuntimeError: Already borrowed at the end.

import asyncio
import signal

import watchfiles


async def watch_files():
    async for changes in watchfiles.awatch("/tmp/foobar"):
        pass


async def main():
    exit_event = asyncio.Event()

    loop = asyncio.get_running_loop()
    for s in [signal.SIGINT, signal.SIGTERM]:
        loop.add_signal_handler(s, exit_event.set)

    print("1) Initiating watch_files")
    task = asyncio.create_task(watch_files())
    await exit_event.wait()

    print("2) SIGINT detected, canceling the task")
    task.cancel()
    done_event = asyncio.Event()
    task.add_done_callback(lambda t: done_event.set())

    print("3) Waiting for the task to finish")
    await done_event.wait()
    print("4) task finished")


if __name__ == "__main__":
    print("** Before main")
    asyncio.run(main())
    print("** After main")

bellini666 avatar Jul 25 '22 22:07 bellini666

Running this on my M1 mac it runs fine without the error. I'll try tomorrow on my linux desktop.

I suspect it's a problem with pyo3 somehow, but I don't want to ask there until we've narrowed it down a bit (or failed to do so).

samuelcolvin avatar Jul 26 '22 18:07 samuelcolvin

Running this on my M1 mac it runs fine without the error. I'll try tomorrow on my linux desktop.

I suspect it's a problem with pyo3 somehow, but I don't want to ask there until we've narrowed it down a bit (or failed to do so).

Very strange, what version of python you are running there? I'm running python 3.10.5

bellini666 avatar Jul 26 '22 18:07 bellini666

Python 3.10.5 😄 .

Just double checking:

  • you installed a wheel from pypi?
  • you're using libc not musl or something weird?

samuelcolvin avatar Jul 26 '22 18:07 samuelcolvin

Yes, I created a fresh venv to run that code and installed watchfiles with pip install watchfiles, which installed the latest wheel

And no weird things, it is the python shipped with debian itself: https://packages.debian.org/sid/python3.10

bellini666 avatar Jul 26 '22 18:07 bellini666

Works fine for me on Linux. One last thing to check before I report to pyo3. You mentioned you're on amd64, I'm on x86_64, could you just run python -c 'import platform; print(platform.platform()); print(platform.version())' in case that's the difference.

My output:

➤  python -c 'import platform; print(platform.platform()); print(platform.version())'
Linux-5.15.0-41-generic-x86_64-with-glibc2.35
#44-Ubuntu SMP Wed Jun 22 14:20:53 UTC 2022

samuelcolvin avatar Jul 27 '22 09:07 samuelcolvin

It's probably just a difference on how Ubuntu and Debian calls, but they should be the same arch.

❯ python3 -c 'import platform; print(platform.platform()); print(platform.version())'
Linux-5.18.0-2-amd64-x86_64-with-glibc2.33
#1 SMP PREEMPT_DYNAMIC Debian 5.18.5-1 (2022-06-16)

And so strange that the issue don't happen to you, it happens literally everytime for me (it is not something that happen "sometimes"). Here is the full testing for reference:

   /tmp                                                                                                                                                                                             at   11:06:22
❯ python3 -m venv venv

   /tmp                                                                                                                                                                                  took   3s at   11:06:52
❯ source venv/bin/activate

   /tmp                                                                                                                                                                                        tmp at   11:06:54
❯ pip install watchfiles
Collecting watchfiles
  Using cached watchfiles-0.16.1-cp37-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.2 MB)
Collecting anyio<4,>=3.0.0
  Using cached anyio-3.6.1-py3-none-any.whl (80 kB)
Collecting sniffio>=1.1
  Using cached sniffio-1.2.0-py3-none-any.whl (10 kB)
Collecting idna>=2.8
  Using cached idna-3.3-py3-none-any.whl (61 kB)
Installing collected packages: sniffio, idna, anyio, watchfiles
Successfully installed anyio-3.6.1 idna-3.3 sniffio-1.2.0 watchfiles-0.16.1

   /tmp                                                                                                                                                                                        tmp at   11:06:58
❯ python3 test_watchfiles.py 
** Before main
1) Initiating watch_files
^C2) SIGINT detected, canceling the task
3) Waiting for the task to finish
4) task finished
** After main
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<watch_files() done, defined at /tmp/test_watchfiles.py:7> exception=RuntimeError('Already borrowed')>
Traceback (most recent call last):
  File "/tmp/test_watchfiles.py", line 8, in watch_files
    async for changes in watchfiles.awatch("/tmp/foobar"):
  File "/tmp/venv/lib/python3.10/site-packages/watchfiles/main.py", line 222, in awatch
    with RustNotify([str(p) for p in paths], debug, force_polling, poll_delay_ms) as watcher:
RuntimeError: Already borrowed

bellini666 avatar Jul 27 '22 14:07 bellini666

I think you should create an issue on pyo3 and see if those guys can help. I think it could be related to

https://github.com/samuelcolvin/watchfiles/blob/7cc903004bbea712a295c6674e728ce27752b0dc/src/lib.rs#L276

But I really don't know.

samuelcolvin avatar Jul 27 '22 16:07 samuelcolvin

Just reported it here: https://github.com/PyO3/pyo3/issues/2525

bellini666 avatar Jul 28 '22 19:07 bellini666

closed since https://github.com/PyO3/pyo3/issues/2525 was closed.

samuelcolvin avatar Aug 16 '22 20:08 samuelcolvin

Thanks for reporting.

To help narrow down the error, please can you check if you get it with v0.15?

On Mon, 25 Jul 2022, 17:59 Thiago Bellini Ribeiro, @.***> wrote:

Description

When exiting my application I'm getting RuntimeError: Already borrowed error Example Code

No response Watchfiles Output

future: <Task finished name='Task-9' coro=<Agent.process_door_events() done, defined at /home/bellini/dev/2u/nomad/nomad/agents/base.py:176> exception=RuntimeError('Already borrowed')> Traceback (most recent call last): File "/home/bellini/dev/2u/nomad/nomad/agents/base.py", line 181, in process_door_events async for door_opened in self.listen_for_door_events(): File "/home/bellini/dev/2u/nomad/nomad/agents/dev.py", line 66, in listen_for_door_events async for changes in watchfiles.awatch(self._incoming_file): File "/home/bellini/dev/2u/nomad/.venv/lib/python3.10/site-packages/watchfiles/main.py", line 222, in awatch with RustNotify([str(p) for p in paths], debug, force_polling, poll_delay_ms) as watcher: RuntimeError: Already borrowed

Operating System & Architecture

Debian Sid amd64 Environment

python3.10 Python & Watchfiles Version

python: 3.10.5 (main, Jun 8 2022, 09:26:22) [GCC 11.3.0], watchfiles: 0.16.0 Rust & Cargo Version

cargo 1.56.0 rustc 1.59.0

— Reply to this email directly, view it on GitHub https://github.com/samuelcolvin/watchfiles/issues/177, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA62GGJI2IEIQY2PM774FZLVV3BYTANCNFSM54S7D3IA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

samuelcolvin avatar Oct 11 '22 08:10 samuelcolvin

Hey all- wanted to report that I can reproduce this under certain circumstances on 0.18:

$ python -c 'import platform; print(platform.platform()); print(platform.version())'
macOS-12.6.1-x86_64-i386-64bit
Darwin Kernel Version 21.6.0: Thu Sep 29 20:12:57 PDT 2022; root:xnu-8020.240.7~1/RELEASE_X86_64
$ pip show watchfiles
Name: watchfiles
Version: 0.18.1
...
#!/usr/bin/env python3
import asyncio, watchfiles

async def watch():
    async for changes in watchfiles.awatch("."):
        for change in changes:
            print(change)

async def main():
    asyncio.create_task(watch())
    await asyncio.sleep(1)

asyncio.run(main())
unhandled exception during asyncio.run() shutdown
task: <Task finished name='Task-2' coro=<watch() done, defined at ./foo.py:6> exception=RuntimeError('Already borrowed')>
Traceback (most recent call last):
  File "./foo.py", line 7, in watch
    async for changes in watchfiles.awatch("."):
  File "/lib/python3.9/site-packages/watchfiles/main.py", line 266, in awatch
    yield changes
RuntimeError: Already borrowed

It seems to only happen when the coroutine using awatch happens in a task; when I asyncio.run(watch()) directly, it responds normally to SIGINT. It doesn't seem to make a difference whether the task is ignored or cancelled and awaited at the end of main():

#!/usr/bin/env python3
import asyncio, watchfiles

async def watch():
    async for changes in watchfiles.awatch("."):
        for change in changes:
            print(change)

async def main():
    t = asyncio.create_task(watch())
    await asyncio.sleep(1)
    t.cancel()
    await t

asyncio.run(main())
Traceback (most recent call last):
  File "./foo.py", line 20, in <module>
    asyncio.run(main())
  File "/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
  File "./foo.py", line 16, in main
    await t
  File "./foo.py", line 7, in watch
    async for changes in watchfiles.awatch("."):
  File "/lib/python3.9/site-packages/watchfiles/main.py", line 266, in awatch
    yield changes
RuntimeError: Already borrowed

Please let me know if I should move this over to the PyO3 thread!

wbadart avatar Nov 17 '22 20:11 wbadart

Thanks so much, to save me the effort, it would be great if you can follow the instructions on the rust thread, and report the output there.

samuelcolvin avatar Nov 17 '22 21:11 samuelcolvin

See also #200 which is still open.

samuelcolvin avatar Nov 17 '22 21:11 samuelcolvin

Will do. Thanks for the quick response!

wbadart avatar Nov 17 '22 22:11 wbadart

https://github.com/PyO3/pyo3/issues/2525#issuecomment-1319298790

wbadart avatar Nov 17 '22 22:11 wbadart