-
Notifications
You must be signed in to change notification settings - Fork 59
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
Support for nng_msg as Message #24
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lookin pretty good to me 👍
pynng/nng.py
Outdated
pipe_id = lib.nng_pipe_id(lib_pipe) | ||
if pipe_id < 0: | ||
# TODO: Better exception | ||
raise Exception('No such pipe') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about PipeNotFoundError
much like ModuleNotFoundError
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First I want to check to see what nng itself does whenever it's given a pipe object that doesn't exist anymore, and see if it makes sense to reuse that exception; otherwise, I like this.
test/test_msg.py
Outdated
to = 1000 | ||
|
||
|
||
def test_socket_send_recv_msg(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it.
test/test_msg.py
Outdated
msg = pipe.new_msg(b'oh hello friend') | ||
assert isinstance(msg, pynng.Message) | ||
assert msg.bytes == b'oh hello friend' | ||
s1.sendmsg(msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm slightly partial to send_msg()
/ recv_msg()
personally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my first version, I did use send_msg
and recv_msg
; I changed it to be more consistent with nng. But you convinced me! I'm going to change it back.
test/test_msg.py
Outdated
wait_pipe_len(s1, 1) | ||
pipe = s1.pipes[0] | ||
msg = pipe.new_msg(b'oh hello friend') | ||
assert msg.pipe is pipe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you could probably just stick these 2 extra pipe assertions in the above test to save some duplication?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth doing. I was trying to narrow the scope of tests, but whenever the test is almost completely duplicated anyway it makes sense to put more into it.
pynng/nng.py
Outdated
flags = 0 | ||
if not block: | ||
flags |= lib.NNG_FLAG_NONBLOCK | ||
lib_sock = self.pipe.socket.socket |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I wonder if it should be Socket.nng_socket
(kinda out of scope)..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm pretty unhappy with that on all the pynng right now. Listener
has a listener
attribute for the underlying nng object, Dialer
has a dialer
attribute, etc. I want to change it from what it is now, but am not sure whether to change it to either the nng library name (so nng_socket
like you suggested here) or give every object a _lib_obj
attribute that is the nng object.
I'm leaning toward the latter, but I'm not confident it's better. Feedback is welcome there!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that seems fine.
The only thing I can say helps me is some prefix that indicates what is ffi
pointers / refs versus native python objects.
I was super wrong for giving the Message a send() method. I was thinking that a message would always need to have a pipe associated, but that's not the case. For example, it may not even be supported to set the pipe for pub/sub. So now socket will have a send_msg() method again. |
I think that more or less wraps up Message support...
Most of the functionality is ironed out now, but the API is not really ready yet; mostly, I think I'll remove the Additionally, I need to not do So a little left to do on this PR, but not too much, I think. Feedback welcome! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just realized this was already merged but figured I'd give it a once over anyway.
Had my head in my own pile of code this weekend, heh.
if pipe_id < 0: | ||
# TODO: Better exception | ||
raise Exception('No such pipe') | ||
pipe = self._pipes[pipe_id] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return self._pipes[pipe_id]
?
with msg._mem_freed_lock: | ||
msg._ensure_can_send() | ||
with _aio.AIOHelper(self, self._async_backend) as aio: | ||
val = await aio.asend_msg(msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return await aio.asend_msg(msg)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, this was cruft leftover from an earlier round of coding.
msg._ensure_can_send() | ||
with _aio.AIOHelper(self, self._socket._async_backend) as aio: | ||
val = await aio.asend_msg(msg) | ||
return val |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again just a single line seems fine.
|
||
def __init__(self, data, pipe=None): | ||
self._pipe = pipe | ||
# NB! There are two ways that a user can free resources that an nng_msg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment might be out of date now?
Re:
So the only way to send a message is with the send() and asend() methods on the object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good observation.
""" | ||
|
||
def __init__(self, data, pipe=None): | ||
self._pipe = pipe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should use the setter property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating this also showed me a bug in my code: I thought you could "unset" a message's pipe by passing NULL, but I never tested it, and it caused problems. (I'm not sure why I ever thought that would be possible; nng_msg_set_pipe
doesn't take a pointer, it takes a nng_pipe
... oh well!)
self._pipe = None | ||
return | ||
if not isinstance(pipe, Pipe): | ||
msg = 'pipe must be type Pipe, not {}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
msg = 'pipe must be type Pipe, not {}'.format(type(pipe))
I mean heck you could do it all in the ValueError
arg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
pynng/nng.py
Outdated
self._pipe = pipe | ||
|
||
@property | ||
def buffer(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it maybe be handy to make this a context manager that will block you from being able to send while the buffer is in use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same idea, but I couldn't come up with an implementation that actually guaranteed preventing using a buffer after sending. Once the user has access to the buffer, there's no way to disable using it. Imagine code like this:
with msg.borrow_buffer() as buf:
do_something_cool(buf)
sock.send_msg(msg)
# can't prevent this
print(buf[0:10])
There is no way to mark a buffer as being invalid. This is a cffi.buffer
type. Maybe it's not worth exposing it at all though? As far as I can tell this is the only place in the library where it would be pretty easy to cause a segfault. I named it _buffer
with an underscore to indicate that it's dangerous, not that it's private; hopefully people would mostly reach for the bytes
property...
if not self._mem_freed: | ||
size = lib.nng_msg_len(self._nng_msg) | ||
data = ffi.cast('char *', lib.nng_msg_body(self._nng_msg)) | ||
return ffi.buffer(data[0:size]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need the 0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, for ffi.buffer objects the start index has to be given.
Thanks for the feedback @tgoodlet! I'll look at it all soon. |
Code cleanup based on review in #24 by @tgoodlet.
This is the beginning of supporting nng_msg in pynng. Related issue is #21.
So far only sychronous methods are supported. The API will change, I think, before this is merged; I think that my
Message
class will grow a publicsend()
method. Instead ofsocket.sendmsg(msg)
, you would instead callmsg.send()
. Feedback welcome. To see how it's used right now, check out the tests!