-
Notifications
You must be signed in to change notification settings - Fork 5
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 specific gRPC client errors #53
Conversation
This is marked as a draft because |
f6dc279
to
03af118
Compare
Signed-off-by: Leandro Lucarella <[email protected]>
This makes error handling more pythonic, as one can now just catch the exception type one is interested in, without having to do a second-level matching using the status. It also helps avoiding to expose the grpclib classes to the user. Signed-off-by: Leandro Lucarella <[email protected]>
Enabling auto-merge. |
@@ -41,14 +60,31 @@ | |||
"ComponentMetricId", | |||
"ComponentType", | |||
"Connection", | |||
"DataLoss", | |||
"EVChargerCableState", |
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.
Optional, but I usually like to organize stuff with comments, when there are so many things inside __all__
, for example:
__all__ = [
#
# Generic types
#
"Component",
"ComponentCategory",
#
# Streaming types
#
"BatteryData",
#
# Exceptions
#
"DataLoss",
]
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.
Doesn't that info comes from the imports already? For me this is all very verbose and duplicated, I really would like to move to use from m import *
in __init__.py
and that's it. It works as expected with mypy
(and official in Python, is part of the typing PEP, PEP484), but sadly mkdocstrings
doesn't support it yet, so we still need to use __all__
if we want symbols to appear in the docs 😢
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.
The list of symbols in __all__
is much more readable from the jumbled import statements where the symbols are not aligned and are grouped by the file they come from and not the category. Please don't remove it.
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.
OK, this is a separate conversation, so let's not have it in this PR, I prefer to focus on this PR here, so I will just focus on having comments in __all__
or not.
For me __all__
is just noise, another way to do pylint: disable=unused-import
or whatever is called. I think the key is to match files to categories, this is basically why we split symbols in different files, so for me looking at the import already gives me all the information I need. IMHO adding the comments to __all__
is adding even more duplication, now I not only have to repeat the y
of from x import y
, I also have to repeat the x
(and if we are not repeating it, we need to re-organize which symbol goes to which file IMHO, not fixing it with comments in __all__
).
That said, if it really adds clarity for you, I can add it, I guess we should try to make stuff as readable as possible for as much people as possible 🤷 🙂
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 is a separate conversation, so let's not have it in this PR, I prefer to focus on this PR here
I didn't start it 😈
For me all is just noise
and you didn't really stop either. 😠
if it really adds clarity for you, I can add it
I like doing it, but I mentioned optional anyway, so it is up to you.
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.
One more thing while I think about this (I was looking into it to see if I would use a different categorization or the same as in files). If these comments are really intended to the users, I think we should add that classification in the module documentation instead, if we do that, we have some duplication, we still need to more or less list all the symbols twice, but I do see much more added value there, if this classification is shown in the docs.
In any case, this this is pre-existing code and not something added by this PR, I would rather do this as a separate PR, when improving the docs.
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.
and you didn't really stop either. 😠
Haha, I meant just about removing __all__
completely.
if it really adds clarity for you, I can add it
I know see the value in the grouping as user documentation, but I don't want to get into writing the docs now that the project will change a lot when the upgrade to v0.17.0 happens.
If you think the current mapping of files/categories makes sense, I could add comments to match the files, that's not a problem as it is very low effort. Just let me know and I will add it, is just a few tab-tab with copilot.
If not, I will need to spend some time thinking which categories are appropriate, and if I do that, I will want to also map those categories to files as it should be. This will be something much more time consuming and again kind of pointless just before the upgrade to v0.17.0.
|
||
Returns: | ||
An instance of | ||
[GrpcStatusError][frequenz.client.microgrid.GrpcStatusError] if |
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 can be UnrecognizedGrpcError
?
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. Do you think it would be useful to catch a UnrecognizedGrpcError
? Otherwise I'm not sure what would be the advantage of giving it a special class. I can add it anyway, I'm not against it, mostly curious if you already had a use case in mind.
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.
oh, didn't realize GrpcStatusError
is a superclass for all the other errors. Why is it not just called GrpcError
, btw?
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 can add it anyway, I'm not against it, mostly curious if you already had a use case in mind.
No, it makes sense the way you've done it, thought the GrpcStatusError
was a separate thing just for the unrecognized case. But also found the Status
part of the error's name weird.
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, actually because GrpcStatusError
is a superclass for all other errors it makes sense to have this separately, otherwise there is no way to tell if the status we received is unrecognized, if you catch GrpcStatusError
, then you'll catch all of the others.
I wondered if it shouldn't be just mapped to UnknownError
, but I think it shouldn't because so far every know gRPC error status is mapped to a single class, and if we do so UnknownError
could have a UNKNOWN
status or any other random int
that we don't know about as status, which is more confusing, so I guess a dedicated error for an unrecognized status still makes sense.
Added a new exception |
I think it should be did you also see my question above about "Why not call it |
OK, I can do that.
Why isn't the error with the status? The error is we don't recognize the status, right? I don't see how this is not an unrecognized status, then we could call it |
No, I meant for the superclass |
OK, I just wanted to emphasize that this error is basically mapping status codes to exceptions, I'm not sure if we could eventually want to raise other types of gRPC errors that are not tied to a status, like if you can't connect to the gRPC service at all, but maybe that is not strictly a gRPC error. I'm open to rename it to |
Updated with |
For that we have
I think we should, to make it clear that a grpc error happened, and not something that happened while dealing with a status. When a service method fails without providing a status or has a panic or something, we'll see the status 'Unknown'. So there will always be a status. grpclib seems to call it |
Pushed the rename. This should be ready for a final LGTM and then I can squash the fixups. |
when the api call exceeded the timeout. | ||
ClientError: If the are any errors communicating with the Microgrid API, | ||
most likely a subclass of | ||
[GrpcStatusError][frequenz.client.microgrid.GrpcStatusError]. |
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.
These need to change too :D
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, right. Now I hope all references are updated.
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.
Now, updated
06a0754
to
48d826b
Compare
Signed-off-by: Leandro Lucarella <[email protected]>
This flag can simplify deciding if an operation returning an error can be blindly retried. Some errors might need some intervention to change the system's state, but another actor might do that, so a bling retry might still succeed. Retrying is still largely missing, but it will be solved separately, see: frequenz-floss#52 Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
I just squashed and pushed because I guess you already did the review of the fixup commits. |
When we receive a gRPC status code that we don't recognize, we raise a `GrpcStatusError` instead of the base class for all gRPC status errors. This will allow users to more easily differentiate between known and unknown status codes. Signed-off-by: Leandro Lucarella <[email protected]>
Also improve the docs for the class slightly to be more clear about which errors should be more protocol-independent and safer to catch. Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
Sorry, one last update to improve the release notes to adjust to the current changes. |
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 is awesome! We can take this further by defining more domain specific error statuses - either using the grpc status mechanism, don't know if that's possible, or through the description. For example,
except FailedPrecondition as e:
if e.desc() == "OVER_SOC":
# allow discharge, but block charge
but that would need to be coordinated with the server side.
Yeah, we can define more meaningful errors in the error description I guess. Or maybe the "details", never looked what is that for and if it can be easily set by the server. I would do it more structurally, and have more exceptions in the hierarchy so we don't need to resort again to inspecting the exception to have a look at another level of type of error, so more like: except BatteryAlreadyFull:
... |
This makes error handling more pythonic, as one can now just catch the exception type one is interested in, without having to do a second-level matching using the status.
It also helps avoiding to expose the grpclib classes to the user.