python-trio / trio

Trio – a friendly Python library for async concurrency and I/O
https://trio.readthedocs.io
Other
5.98k stars 325 forks source link

AttributeError: type object 'GreenSocket' has no attribute 'sendmsg' #3015

Open maldororxul opened 3 weeks ago

maldororxul commented 3 weeks ago

I'm trying to work with openai package that uses trio on Flask app hosted on Heroku.

First it failed with 'epoll issue': https://github.com/python-trio/trio/issues/2848 I tired to fix it with monkey_patch, but got a new issue:

AttributeError: type object 'GreenSocket' has no attribute 'sendmsg'

My entry point with patch:

import eventlet
import sys
from types import ModuleType

# Apply eventlet monkey patching
eventlet.monkey_patch()

def monkey_patch_trio():
    try:
        import select
        if not hasattr(select, 'epoll'):
            class FakeEpoll:
                def __init__(self, *args, **kwargs):
                    raise NotImplementedError("epoll is not supported on this platform")

            fake_select_module = ModuleType("select")
            fake_select_module.epoll = FakeEpoll
            sys.modules["select"] = fake_select_module

    except ImportError:
        pass

monkey_patch_trio()

# Import the rest of the application after monkey patching and Flask App creating

Dependencies:

trio==0.25.1
trio-websocket==0.10.3
openai==1.30.5
eventlet==0.30.2

Error logs:

2024-06-10T13:16:50.217688+00:00 app[web.1]: Traceback (most recent call last):
2024-06-10T13:16:50.217689+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 2190, in wsgi_app
2024-06-10T13:16:50.217689+00:00 app[web.1]:     response = self.full_dispatch_request()
2024-06-10T13:16:50.217690+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 1486, in full_dispatch_request
2024-06-10T13:16:50.217690+00:00 app[web.1]:     rv = self.handle_user_exception(e)
2024-06-10T13:16:50.217691+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask_cors/extension.py", line 176, in wrapped_function
2024-06-10T13:16:50.217691+00:00 app[web.1]:     return cors_after_request(app.make_response(f(*args, **kwargs)))
2024-06-10T13:16:50.217691+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 1484, in full_dispatch_request
2024-06-10T13:16:50.217692+00:00 app[web.1]:     rv = self.dispatch_request()
2024-06-10T13:16:50.217692+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 1469, in dispatch_request
2024-06-10T13:16:50.217693+00:00 app[web.1]:     return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
2024-06-10T13:16:50.217693+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask_login/utils.py", line 290, in decorated_view
2024-06-10T13:16:50.217693+00:00 app[web.1]:     return current_app.ensure_sync(func)(*args, **kwargs)
2024-06-10T13:16:50.217694+00:00 app[web.1]:   File "/app/app/main/routes/calls_recognition.py", line 14, in add_calls_to_queue
2024-06-10T13:16:50.217695+00:00 app[web.1]:     CallsRecognitionManager(branch='sm').add_calls_from_amo(_filter={'contact_id': request.args.get('contact_id')})
2024-06-10T13:16:50.217695+00:00 app[web.1]:   File "/app/app/calls_recognition/manager.py", line 24, in __init__
2024-06-10T13:16:50.217695+00:00 app[web.1]:     self.calls_data_client = CallsDataClient(schema=branch)
2024-06-10T13:16:50.217695+00:00 app[web.1]:   File "/app/app/calls_recognition/data_client.py", line 45, in __init__
2024-06-10T13:16:50.217695+00:00 app[web.1]:     self.gpt_processor = GPTProcessor()
2024-06-10T13:16:50.217695+00:00 app[web.1]:   File "/app/app/calls_recognition/gpt_processor.py", line 11, in __init__

Here starts the problem:

2024-06-10T13:16:50.217696+00:00 app[web.1]:     from openai import OpenAI
2024-06-10T13:16:50.217696+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/openai/__init__.py", line 8, in <module>
2024-06-10T13:16:50.217697+00:00 app[web.1]:     from . import types
2024-06-10T13:16:50.217697+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/openai/types/__init__.py", line 5, in <module>
2024-06-10T13:16:50.217697+00:00 app[web.1]:     from .batch import Batch as Batch
2024-06-10T13:16:50.217697+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/openai/types/batch.py", line 7, in <module>
2024-06-10T13:16:50.217697+00:00 app[web.1]:     from .._models import BaseModel
2024-06-10T13:16:50.217697+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/openai/_models.py", line 24, in <module>
2024-06-10T13:16:50.217697+00:00 app[web.1]:     from ._types import (
2024-06-10T13:16:50.217698+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/openai/_types.py", line 21, in <module>
2024-06-10T13:16:50.217698+00:00 app[web.1]:     import httpx
2024-06-10T13:16:50.217698+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpx/__init__.py", line 2, in <module>
2024-06-10T13:16:50.217698+00:00 app[web.1]:     from ._api import delete, get, head, options, patch, post, put, request, stream
2024-06-10T13:16:50.217698+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpx/_api.py", line 4, in <module>
2024-06-10T13:16:50.217699+00:00 app[web.1]:     from ._client import Client
2024-06-10T13:16:50.217699+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpx/_client.py", line 30, in <module>
2024-06-10T13:16:50.217699+00:00 app[web.1]:     from ._transports.default import AsyncHTTPTransport, HTTPTransport
2024-06-10T13:16:50.217699+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpx/_transports/default.py", line 30, in <module>
2024-06-10T13:16:50.217699+00:00 app[web.1]:     import httpcore
2024-06-10T13:16:50.217699+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpcore/__init__.py", line 1, in <module>
2024-06-10T13:16:50.217699+00:00 app[web.1]:     from ._api import request, stream
2024-06-10T13:16:50.217700+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpcore/_api.py", line 5, in <module>
2024-06-10T13:16:50.217700+00:00 app[web.1]:     from ._sync.connection_pool import ConnectionPool
2024-06-10T13:16:50.217700+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpcore/_sync/__init__.py", line 1, in <module>
2024-06-10T13:16:50.217700+00:00 app[web.1]:     from .connection import HTTPConnection
2024-06-10T13:16:50.217700+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpcore/_sync/connection.py", line 12, in <module>
2024-06-10T13:16:50.217700+00:00 app[web.1]:     from .._synchronization import Lock
2024-06-10T13:16:50.217701+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/httpcore/_synchronization.py", line 13, in <module>
2024-06-10T13:16:50.217701+00:00 app[web.1]:     import trio
2024-06-10T13:16:50.217701+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/trio/__init__.py", line 26, in <module>
2024-06-10T13:16:50.217701+00:00 app[web.1]:     from . import abc, from_thread, lowlevel, socket, to_thread
2024-06-10T13:16:50.217702+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/trio/socket.py", line 16, in <module>
2024-06-10T13:16:50.217702+00:00 app[web.1]:     from . import _socket
2024-06-10T13:16:50.217702+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/trio/_socket.py", line 526, in <module>
2024-06-10T13:16:50.217702+00:00 app[web.1]:     class SocketType:
2024-06-10T13:16:50.217702+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/trio/_socket.py", line 719, in SocketType
2024-06-10T13:16:50.217705+00:00 app[web.1]:     @_wraps(_stdlib_socket.socket.sendmsg, assigned=(), updated=())
2024-06-10T13:16:50.217705+00:00 app[web.1]: AttributeError: type object 'GreenSocket' has no attribute 'sendmsg'
A5rocks commented 3 weeks ago

As a quick fix you can probably import trio before monkey patching. This will cache trio with un-patched system things.

There has to be a way to fix this that isn't just a game of whack-a-mole but I don't have any ideas.

maldororxul commented 3 weeks ago

If I import trio before monkey patching it will fall with "AttributeError: module 'select' has no attribute 'epoll'".

I ended up with this to avoid both attribute errors:

def monkey_patch_trio():
    try:
        import select
        if not hasattr(select, 'epoll'):
            class FakeEpoll:
                def __init__(self, *args, **kwargs):
                    raise NotImplementedError("epoll is not supported on this platform")

            fake_select_module = ModuleType("select")
            fake_select_module.epoll = FakeEpoll
            sys.modules["select"] = fake_select_module

        # Patch GreenSocket before trio._socket is imported
        from eventlet.greenio.base import GreenSocket as EventletGreenSocket

        if not hasattr(EventletGreenSocket, 'sendmsg'):
            setattr(EventletGreenSocket, 'sendmsg', lambda *args, **kwargs: None)
        if not hasattr(EventletGreenSocket, 'recvmsg'):
            setattr(EventletGreenSocket, 'recvmsg', lambda *args, **kwargs: None)
        if not hasattr(EventletGreenSocket, 'recvmsg_into'):
            setattr(EventletGreenSocket, 'recvmsg_into', lambda *args, **kwargs: None)

        # Now import trio._socket
        import trio._socket as trio_socket
        import socket as _stdlib_socket
        from functools import wraps as _wraps

        class PatchedSocketType(trio_socket.SocketType):
            @_wraps(_stdlib_socket.socket.sendmsg, assigned=(), updated=())
            def sendmsg(self, *args, **kwargs):
                raise NotImplementedError("sendmsg is not supported on this platform")

            @_wraps(_stdlib_socket.socket.recvmsg, assigned=(), updated=())
            def recvmsg(self, *args, **kwargs):
                raise NotImplementedError("recvmsg is not supported on this platform")

            @_wraps(_stdlib_socket.socket.recvmsg_into, assigned=(), updated=())
            def recvmsg_into(self, *args, **kwargs):
                raise NotImplementedError("recvmsg_into is not supported on this platform")

        trio_socket.SocketType = PatchedSocketType

    except ImportError:
        pass
A5rocks commented 3 weeks ago

Sorry, I mean import trio before running eventlet.monkey_patch().