1

I've gotten ZMQ to work in .py files but I want to use it in a cython class but have not figure out how to import the right library in to .pyx files. Example:

import zmq
from zmq.backend.cython.socket cimport Socket
from zmq.backend.cython.context import Context

cdef class MyClass:

    cdef public str tick_port
    cdef public Socket socket
    cdef public Context context

    def __cinit__(self,tick_port):

        self.tick_port = tick_port

        self.context = zmq.Context()
        self.socket = self.context.socket(zmq.SUB)
        self.socket.setsockopt(zmq.SUBSCRIBE, b"")  # Note.

    
    def StartData(self):
        ''' market data comes in here '''
        self.socket.connect("tcp://127.0.0.1:" + str(self.tick_port))
        while True:
            tick_message = self.socket.recv_json()
            print("Data: " + str(tick_message))
            #self.OnTick(eval(tick_message))

how do you correctly declare and define various zmq variables within cython? so I still create context and socket objects like in python? Lots of newbie question but I couldn't find anything from googling "how to use zmq in cython".

for example when compiling the above I get error:

C attribute of type '<error>' cannot be accessed from pythong.

Thanks,

user1234440
  • 22,521
  • 18
  • 61
  • 103
  • 3
    It should be probably `cimport` in `from zmq.backend.cython.context import Context`. Also having a real minimal [mre] and a full error message would make the question clearer. – ead May 17 '21 at 05:58
  • There's probably little to be gained from this: it looks to me like they mainly use Cython to create a Python interface. I doubt if there's much advantage to using it from Cython rather than Python. You can always type things as `object` in Cython – DavidW May 17 '21 at 07:42
  • ahh didn't know about the `object` variable. thats solves my problem thanks. – user1234440 May 17 '21 at 21:35

1 Answers1

0

Things to note:

  1. As mentioned in comments by user DavidW, it appears the Cython code utilizes Python objects & therefore using the Cython module directly from Cython doesn't benefit from possible speedups & releasing the GIL, etc. So just using cdef object context = zmq.Context() and cdef object socket = context.Socket(...) (etc.) is a fine way to go. If you wanted to speed things up or achieve total releasing of the Python GIL within a function, you might need to wrap/call the CZMQ or libzmq libraries from your own Cython code.
  2. You can still do what you were trying to do above & use the Cython types from Cython code. However:
    • As mentioned by user ead in comments, you have to cimport Context similar to how you cimport-ed Socket
    • The Socket and Context types within zmq.backend.cython have different interfaces from the high-level zmq.Socket and zmq.Context classes. You will have to look at the code in the corresponding .pyx files for full details, but for the example above:
      • The Cython Context class has no socket method. You have to instantiate a Socket manually, and the first argument is context=..., which you will use to specify the Context you instantiated prior.
      • The Cython Socket class has no setsockopt method, but does have an equivalent set method.
      • The Cython Socket class has no recv_json method, so you will have to use recv and then convert the returned value to JSON using another library.

See files context.pyx and socket.pyx in the codebase here: https://github.com/zeromq/pyzmq/tree/main/zmq/backend/cython

A corrected version of your code follows:

import zmq
from zmq.backend.cython.socket cimport Socket
from zmq.backend.cython.context cimport Context

cdef class MyClass:

    cdef public str tick_port
    cdef public Socket socket
    cdef public Context context

    def __cinit__(self,tick_port):

        self.tick_port = tick_port

        self.context = Context()
        self.socket = Socket(self.context, zmq.SUB)
        self.socket.set(zmq.SUBSCRIBE, b"")  # Note.

    
    def StartData(self):
        ''' market data comes in here '''
        self.socket.connect("tcp://127.0.0.1:" + str(self.tick_port))
        while True:
            tick_message = self.socket.recv()
            print("Data: " + str(tick_message))
            #self.OnTick(eval(tick_message))