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

zmq.asyncio.Context.instance() returns synchronous version of zmq.Context object #1172

Closed
achimnol opened this issue Apr 27, 2018 · 4 comments · Fixed by #1385
Closed

zmq.asyncio.Context.instance() returns synchronous version of zmq.Context object #1172

achimnol opened this issue Apr 27, 2018 · 4 comments · Fixed by #1385

Comments

@achimnol
Copy link

achimnol commented Apr 27, 2018

  • pyzmq: 17.0.0
  • Python: 3.6.5
  • Installed via pip on macOS High Sierra

image
image

I believe it should return an async-compatible (zmq.asyncio.Context) global object instead of zmq.sugar.context.Context global object.
If this is an intended behavior, it must be explicitly documented here.

@imraanparker
Copy link

The asyncio context inherits from zmq.Context and only overrides the Socket class. In zmq.asyncio

class Context(_zmq.Context):
    """Context for creating asyncio-compatible Sockets"""
    _socket_class = Socket

It does not override the instance() method, hence why you see the sugar context object as the method is called on the parent class. It however is the asyncio Context as the socket class is the asyncio one.

A simple test will show. If I change the asyncio Context class as below:

class Context(_zmq.Context):
    """Context for creating asyncio-compatible Sockets"""
    _socket_class = Socket
    
    @classmethod
    def instance(cls, io_threads=1):
        return super(Context, self).instance(cls, io_threads)

I am overriding the method and manually calling the parent whereas python would do that automatically. Your example now displays correctly.

import zmq
import zmq.asyncio
zmq.asyncio.Context.instance()
<zmq.asyncio.Context object at 0x7f2524501f98>
zmq.Context.instance()
<zmq.sugar.context.Context object at 0x7f25242c5198>

I think there is an issue with repr() of a class when it is created with a @classmethod method and inheritance. We could fix the repr() with my update above, but that feels wrong.

@chriscline
Copy link

This behavior is problematic, as socket objects returned by zmq.asyncio.Context.instance().socket() are zmq.Socket type not zmq.asyncio.Socket (with pyzmq version 17.1.2):

Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
>>> import zmq
>>> import zmq.asyncio
>>> zmq.Context.instance()
<zmq.sugar.context.Context object at 0x0000020035D97D68>
>>> zmq.asyncio.Context.instance()
<zmq.sugar.context.Context object at 0x0000020035D97D68>
>>> zmq.asyncio.Context.instance().socket(zmq.SUB)
<zmq.sugar.socket.Socket object at 0x0000020035DAD250>
>>> zmq.asyncio.Context().socket(zmq.SUB)
<zmq.asyncio.Socket object at 0x0000020035E06048>

@Kipsora
Copy link

Kipsora commented Oct 28, 2018

There are also some occasions that an zmq.asyncio.Context could not be used. For example, say we want to get zmq.asyncio.Socket in a child thread with shared context. Then the context must be created by zmq.asyncio.Context which means that all of your sockets (in other threads) must be instance of zmq.asyncio.Socket.

@chtseac
Copy link

chtseac commented Mar 27, 2019

The problem is that the two classes share the same singleton instance, which is located in zmq.Context._instance. If you invoke the instance() function in the asyncio class first, you would get two different contexts because the classfunction uses cls._instance = ... to assign the instance.

In my particular use case, the two Context instances are not used at the same time, thus having two separate Context instances is acceptable. The following workaround makes this always the case:

import zmq
import zmq.asyncio as zmqa

# delete this if the code isn't executed too frequently
if '_instance' not in zmqa.Context:
    # delete this if the code doesn't need to be thread-safe
    # also, this is actually zmq.Context._instance_lock
    with zmqa.Context._instance_lock:
        if '_instance' not in zmqa.Context:
            zmqa.Context._instance = None

# as long as the code above has been run, the below two functions always return
# different contexts of their respective class regardless of order
zmq.Context.instance()
zmqa.Context.instance()

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

Successfully merging a pull request may close this issue.

5 participants