trio icon indicating copy to clipboard operation
trio copied to clipboard

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

Open maldororxul opened this issue 1 year ago • 3 comments

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'

maldororxul avatar Jun 10 '24 13:06 maldororxul

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.

A5rocks avatar Jun 10 '24 14:06 A5rocks

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

maldororxul avatar Jun 11 '24 06:06 maldororxul

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

A5rocks avatar Jun 11 '24 14:06 A5rocks

Ok I thought about this some more and this is totally just an eventlet bug. They claim GreenSocket is "100% api compatible with socket.socket":

https://github.com/eventlet/eventlet/blob/8bac9b2bb5ba02d42305446327a117ff51af177b/eventlet/greenio/base.py#L119-L122

A5rocks avatar Aug 02 '24 21:08 A5rocks

Hi, ich sehe, dass der Post zwar schon ein wenig her ist, aber ich habe soeben mit Darkgpt das selbe Problem gehabt. Und zwar hatte ich folgende Fehlermeldung.

Ich habe aber auch die Lösung,oder eine Lösung. Führt Darkgpt, in einer Virtuellen Python Umgebung aus, dann funktioniert es. Bei mir zumindest.

  File "/home/.local/lib/python3.12/site-packages/trio/__init__.py", line 26, in <module>
    from . import abc, from_thread, lowlevel, socket, to_thread
  File "/home/.local/lib/python3.12/site-packages/trio/socket.py", line 16, in <module>
    from . import _socket
  File "/home/.local/lib/python3.12/site-packages/trio/_socket.py", line 526, in <module>
    class SocketType:
  File "/home/.local/lib/python3.12/site-packages/trio/_socket.py", line 719, in SocketType
    @_wraps(_stdlib_socket.socket.sendmsg, assigned=(), updated=())
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'GreenSocket' has no attribute 'sendmsg'

c3lsi0r avatar Dec 14 '24 15:12 c3lsi0r

Added import trio to the top of eventlet/patcher.py and this resolved AttributeError: type object 'GreenSocket' has no attribute 'sendmsg' in mutltiple python apps.

hbfs avatar May 13 '25 03:05 hbfs

This bug is really weird. I don't know what triggers it exactly, but I've just stumbled upon it in manhole (https://github.com/ionelmc/python-manhole/issues/70), which tries really hard to figure out the original socket, and essentially imports eventlet just for that. The workaround there is to just ignore this exception altogether and move on (https://github.com/ionelmc/python-manhole/pull/71).

We've also seen this in unrelated packages in Debian, first filed against cumin and thought to be related to openstack, but now i'm thinking it's a broader, eventlet related problem: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1093157

anarcat avatar May 29 '25 16:05 anarcat

What I've concluded is that this is a fundamental problem with how eventlet monkeypatches things and that there isn't really anything Trio can do on its end to solve it, and eventlet itself says not to use it for new projects and for all intents and purposes can be considered archived. Best solution I've seen so far is to import trio before anything else so that by the time eventlet tries to monkeypatch sockets trio has already completed what it needs to to initialize properly. Trio and eventlet are two very different async frameworks and things won't work between the two anyways, this is only a solution for on-import errors.

CoolCat467 avatar May 29 '25 22:05 CoolCat467