miguelgrinberg / greenletio

Asyncio integration with sync code using greenlets.
MIT License
150 stars 8 forks source link

AttributeError: 'Lock' object has no attribute '_at_fork_reinit' #12

Open spumer opened 1 year ago

spumer commented 1 year ago

❯ python -V Python 3.11.4 ❯ greenletio run.py web

Output ``` File "run.py", line 7, in import uvicorn File ".venv/lib/python3.11/site-packages/uvicorn/__init__.py", line 1, in from uvicorn.config import Config File ".venv/lib/python3.11/site-packages/uvicorn/config.py", line 36, in from uvicorn.middleware.wsgi import WSGIMiddleware File ".venv/lib/python3.11/site-packages/uvicorn/middleware/wsgi.py", line 85, in class WSGIResponder: File ".venv/lib/python3.11/site-packages/uvicorn/middleware/wsgi.py", line 89, in WSGIResponder executor: concurrent.futures.ThreadPoolExecutor, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/Cellar/python@3.11/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/__init__.py", line 49, in __getattr__ from .thread import ThreadPoolExecutor as te File "/usr/local/Cellar/python@3.11/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/thread.py", line 42, in after_in_child=_global_shutdown_lock._at_fork_reinit, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'Lock' object has no attribute '_at_fork_reinit' ```
miguelgrinberg commented 1 year ago

@spumer the greenletio run command is intended to be used with sync applications. You are using it to start Uvicorn, which is not an intended use case.

spumer commented 1 year ago

I think this error independent of application type. Because ThreadPoolExecutor can't be imported

miguelgrinberg commented 1 year ago

Maybe, yes, that is possible. When you use greenletio run many of the Python standard classes are replaced with alternative implementations, which are not identical. The ThreadPoolExecutor class is not currently patched and it accesses private attributes of the Lock class which exist in the original implementation but not in the green version.

At this time only a short list of modules are patched, several more need to be reviewed and patched. Until then, some things are expected to fail.

spumer commented 1 year ago

Mmm, i will try to implement this protected method.

Looks like we just need recreate internal asyncio.Lock in this case

miguelgrinberg commented 1 year ago

First of all, this package implements the regular Lock class, from threading. The asyncio.Lock class does not benefit from this package at all. The lock in question is here.

Second, it is not a primary goal of this library to provide compatibility with private attributes. The idea is to provide compatibility for public attributes and methods. Code that makes assumptions about the internals of other classes is not expected to work. It may not be possible to implement the same private attributes at all, due to the big difference in concurrency models. To address these usages between different modules of the Python standard library, it may be necessary to also patch the module that fails.

spumer commented 1 year ago

Ok, i understand. You are right

spumer commented 1 year ago

Can we convert this discussion to support concurrent.futures.ThreadPoolExecutor or open the new one and close this?

miguelgrinberg commented 1 year ago

There are many parts of the standard library that need to be implemented. In fact, only a small portion is available right now. So yes, the thread pool executor should eventually work. But as I said in the beginning of this thread, what you are doing is wrong. Running uvicorn under a patched library makes no sense and is unlikely to ever be supported.

spumer commented 1 year ago

I'm search the way to run sync handlers in fastapi(Fastapi allow write async and sync handlers) through gevent instead real threads (default fastapi behaviour for sync handler) and make app fully async.

Also i want to check how performance changed under load testing this setup.

miguelgrinberg commented 1 year ago

The monkey patching feature of this package is not really the way to do what you want. The first step is probably to add the @async_ decorator to your handlers, so that FastAPI runs them as async functions. The @async_ decorator will do all the greenlet magic to prevent the loop from blocking. At least in theory, I haven't tested this with FastAPI.

spumer commented 1 year ago

Yes, but i still need monkey patching to work decorated functions correctly. And this is why i run uvicorn, and this is why this issue created :)

miguelgrinberg commented 1 year ago

@spumer We are going in circles here. The monkey patching is not intended to be used with async applications such as uvicorn or FastAPI, as I indicated above. Monkey patching is for sync applications.

If you have sync code that runs as part of your uvicorn/fastapi app that needs to use greenletio's functions, then you can import them directly from the greenletio.green packages, or else you can use the patch_blocking context manager to perform a controlled monkey patching that is local to your sync functions, without affecting the overall async environment.

If you are intending to use this package to somehow alter how FastAPI deals with sync functions, then I'm afraid that is not an intended goal. You may be able to make FastAPI think you sync functions are async with the @async_ decorator, but that is the extent of it. I have no plans to make this an extension to FastAPI.