-
Notifications
You must be signed in to change notification settings - Fork 56
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
Add abbility to serialize and deserialize messages #84
Add abbility to serialize and deserialize messages #84
Conversation
f511800
to
c827d42
Compare
Codecov Report
@@ Coverage Diff @@
## master #84 +/- ##
==========================================
+ Coverage 97.19% 97.38% +0.18%
==========================================
Files 11 11
Lines 927 993 +66
Branches 127 129 +2
==========================================
+ Hits 901 967 +66
Misses 17 17
Partials 9 9
Continue to review full report at Codecov.
|
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.
Thanks for this. I left a lot of comments, but none of them are major.
fedora_messaging/message.py
Outdated
str: Serialized messages. | ||
|
||
Raises: | ||
MsgDumpError: If at least one message is not instance of Message class or subclass, or |
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 think it'd be best to not have our own exception class for this. Since the messages can't pass validation if they're not valid JSON, the programmer has messed up here and shouldn't be handling this sort of exception. Best to let them just get a JSON encoding error directly.
fedora_messaging/message.py
Outdated
serialized_messages.append(message_json) | ||
except AttributeError: | ||
_log.error("Improper object for messages serialization.") | ||
raise MsgDumpError("Message have to be instance of Message class or subclass.") |
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 best to just raise a "TypeError" here
fedora_messaging/message.py
Outdated
|
||
try: | ||
return json.dumps(serialized_messages) | ||
except Exception as e: |
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.
Since I recommend we don't raise a MsgDumpError
, you can take this out of the try/except. Just as a note, though, it's best not to except Exception
unless you really do mean to handle all exceptions (often done at the entry point to a program). Here we only would want to handle JSON-related errors since that makes it explicit what problems we're expecting to encounter here.
fedora_messaging/message.py
Outdated
list: Deserialized message objects. | ||
|
||
Raises: | ||
MsgLoadError: Loading serialized from JSON is imposiible. |
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.
Same note here about just letting json raise its exception as above
fedora_messaging/message.py
Outdated
serialized_messages = [] | ||
try: | ||
for message in messages: | ||
message_json = message.dump() |
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.
dump()
returns a dictionary so this variable name is a bit confusing - a reader might think this is actually a JSON string.
fedora_messaging/message.py
Outdated
""" | ||
try: | ||
deserialized_messages = [ | ||
message_str for message_str in json.loads(serialized_messages) |
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.
Since we dumped an array, shouldn't this just be message_dicts = json.loads(serialized_messages)
?
try: | ||
headers = msg_json["headers"] | ||
except KeyError: | ||
_log.error("Message saved without headers.") |
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 inclined to say this should just be an exception - whatever got handed in is not in the format we expect.
try: | ||
MessageClass = get_class(headers["fedora_messaging_schema"]) | ||
except KeyError: | ||
_log.error("Message (headers=%r) saved without a schema header.", headers) |
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 let this be an exception. Whatever we got aren't properly serialized messages.
Returns: | ||
dict: A dictionary of messge attributes. | ||
""" | ||
return { |
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.
It'd probably be nice to save the message's id as well, so test writers who want to use this functionality can rely on stable message ids.
c827d42
to
b711de7
Compare
PR is updated. |
b711de7
to
ca2fa95
Compare
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.
Sorry about leaving this so long, I've been neglecting my code review emails!
There are a few more cases where I don't think we should handle errors and set defaults on loading, but otherwise I think this looks solid. Can you write unit tests for the new functions, as well?
Thanks!
|
||
try: | ||
body = message_dict["body"] | ||
except KeyError: |
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 don't think we should default this to something, just let the KeyError propagate.
try: | ||
id = message_dict["id"] | ||
except KeyError: | ||
_log.error("Message saved without 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.
Also here, messages should always have an id so this should just let the KeyError get raised.
fedora_messaging/message.py
Outdated
queue = message_dict["queue"] | ||
except KeyError: | ||
_log.error("Message saved without queue.") | ||
queue = {} |
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 should probably default to None
fedora_messaging/message.py
Outdated
try: | ||
topic = message_dict["topic"] | ||
except KeyError: | ||
_log.error("Message saved without body.") |
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 should just let the KeyError get raised
fedora_messaging/message.py
Outdated
try: | ||
severity = headers["fedora_messaging_severity"] | ||
except KeyError: | ||
_log.error("Message saved without a severity. Defaulting to INFO.") |
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 think we should let the KeyError raise here as well. The reason the other code defaults it is because right now all the messages are being sent in fedmsg without it, so we fix things up for that.
fedora_messaging/message.py
Outdated
raise ValidationError(e) | ||
|
||
message.queue = queue | ||
if 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.
If we assume id will always be set, you can drop this if
Signed-off-by: Sebastian Wojciechowski <s.wojciechowski89@gmail.com>
Signed-off-by: Sebastian Wojciechowski <s.wojciechowski89@gmail.com>
ca2fa95
to
17b16c4
Compare
@jeremycline Do you know how to fix "MessageLoadsTests.test_validation_failure"? |
Don't rely on the equality check implementation of ValidationError Signed-off-by: Jeremy Cline <jcline@redhat.com>
@sebwoj, hey, my guess is the equality method on the exceptions isn't implemented. I don't recall what Python does off the top of my head, but doing |
Signed-off-by: Sebastian Wojciechowski <s.wojciechowski89@gmail.com>
Signed-off-by: Sebastian Wojciechowski <s.wojciechowski89@gmail.com>
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.
There's a minor spelling mistake and I'm curious about your thoughts on the interface, but this looks really good.
fedora_messaging/message.py
Outdated
Dump message attributes. | ||
|
||
Returns: | ||
dict: A dictionary of messge attributes. |
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.
"messge" -> "message"
fedora_messaging/message.py
Outdated
@@ -510,3 +618,18 @@ def flatpaks(self): | |||
list(str): A list of affected flatpaks names. | |||
""" | |||
return [] | |||
|
|||
def dump(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.
I wonder if this is going to be an API most people want to use, or if they should only use the "dumps" and "loads" interfaces outside the class. Maybe we should mark this as private (call it _dump
) initially so we don't pollute the namespace with stuff most people won't want to use. What do you think?
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.
As far as I know(c++ exp) private methods are not available outside the class(except friends). So, if we are using "Message.dump" in "dumps" function, it means that we have to allow access from outside. I don't have a big experience in python and I know that underscore(even dunder) can't prevent clients from access private methods, so I'm not sure about final solution. I would like to let you decide in this topic.
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.
Ah, yes, Python has no way to force clients to not touch internals of objects, it's more like guidelines. I think we should make it private (to begin with, anyway) since I can't think of a reason people should be using it.
Signed-off-by: Sebastian Wojciechowski <s.wojciechowski89@gmail.com>
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.
✨ 🍰 ✨
Signed-off-by: Sebastian Wojciechowski s.wojciechowski89@gmail.com