refactor(API): have config_status return status code and text.#233
refactor(API): have config_status return status code and text.#233
Conversation
|
FYI "read state" is another case where status codes are returned. When I added support for it, I ended up making it a See
Should these different cases be consistent, one way or the other? Or is there a reason for us to take distinct approaches? |
|
Regarding Ed's comment on "read state": I talked to numerous people about this and got varying opinions. George H. was of was of the opinion that we should just return Fault from "read state" and not throw an exception because non-zero Fault indicates a network issue; not an XNET/driver issue. I think this needs a bit more research to determine what makes the most sense for a python user. I'm thinking I'll probably open a separate issue on what to do with Fault. |
|
Clearly, what we currently have for config_status is insufficient. We return a status code and no way for the user to call nx_status_to_string to get text for the code. Here are a few options:
Other options? Thoughts? |
061e66b to
8a6a277
Compare
|
I could see us going either way on this. |
|
Throwing an exception with the status code and error message seems better to me than returning a tuple |
|
I've looked into this a bit more. If we used tuples, the code might look something like this: Keep in mind these could be named tuples too. If we throw an exception instead, the code looks like this: I ran tests on a database with an invalid signal, and the output for both of these looks like this: This exposes a few issues with the code, which are as follows:
|
8a6a277 to
2978216
Compare
|
The last commit was just to show how I would be implementing the |
For # 1 and 2, would it make sense to open a separate issue and track it outside of this PR? I don't think it should be gating the config_status stuff. For # 3, "how should warning codes be handled when calling check_config_status?" I was wondering the same thing. Maybe we should come up with an API guideline for how to expose check_status or check_whatever type of functionality to the Python API user. I have some ideas here but I can discuss those later after this release. For consistency with |
c388a40 to
ac39a1c
Compare
BREAKING CHANGE: Before, we just returned the status code. However, the user previously had no way to get the text for the status code. Now, return the status code and text in a tuple.
or issues an XnetWarning if status is non-zero.
ad533b5 to
2175e60
Compare
|
I filed issue #250 for the previously mentioned status code issues, and modified |
nixnet/_utils.py
Outdated
| _errors.check_for_error(_cconsts.NX_ERR_INVALID_PROPERTY_VALUE) | ||
| error_code = _cconsts.NX_ERR_INVALID_PROPERTY_VALUE | ||
| error_text = _errors.status_to_string(error_code) | ||
| raise errors.XnetError(error_text, error_code) |
There was a problem hiding this comment.
Why are you no longer calling check_for_error?
There was a problem hiding this comment.
I see there is a dedicated commit for this but nothing in the message gives an explanation for it.
There was a problem hiding this comment.
Why do you want check_for_error to be called? If I know I want to raise an error, why would I call a method that conditionally raises or warns instead of raising the error directly? Originally, I thought this was the "abuse" you were mentioning in the comments, though given your questioning here I suspect you meant the abuse was that you just picked the best fitting error code. That wasn't clear to me.
Side note: in another PR I have, calling check_for_error instead of raising directly confuses mypy into complaining that there is a path without a return value.
There was a problem hiding this comment.
Why do you want check_for_error to be called? If I know I want to raise an error, why would I call a method that conditionally raises or warns instead of raising the error directly?
My primary concern is that changes are explained in the commit message.
My (slight) preference for check_for_error is that it is a one-stop shop for getting the appropriate behavior in the correct way. I kept XnetError's __init__ "low level" for the sake of being easy to create whatever type of XnetError we need and assumed everyone would create them via check_for_error.
Originally, I thought this was the "abuse" you were mentioning in the comments, though given your questioning here I suspect you meant the abuse was that you just picked the best fitting error code. That wasn't clear to me.
Yes, it is referring to the error code
Side note: in another PR I have, calling check_for_error instead of raising directly confuses mypy into complaining that there is a path without a return value.
One way of resolving this is we could add a dependency on mypy_extensions and use its NoReturn type annotation.
On the other hand, check_for_error is not unconditionally a NoReturn due to the warning case. Hmmm that is one thing in favor of not using check_for_error.
There was a problem hiding this comment.
What would you think about adding a method in _errors.py
def raise_error(error_code):
that creates XnetError and raises it. Then, those two methods in _errors.py would be our one-stop shop and my hope is that mypy would be smart enough to figure out that a code path involving raise_error unconditionally throws.
There was a problem hiding this comment.
I added a new method raise_xnet_error to use when we unconditionally want an error, and which is called by check_for_error. This also resolve the mypy return issue.
nixnet/_utils.py
Outdated
| _errors.check_for_error(_cconsts.NX_ERR_INVALID_PROPERTY_VALUE) | ||
| error_code = _cconsts.NX_ERR_INVALID_PROPERTY_VALUE | ||
| error_text = _errors.status_to_string(error_code) | ||
| raise errors.XnetError(error_text, error_code) |
nixnet/database/_frame.py
Outdated
| _errors.check_for_error(_cconsts.NX_ERR_SIGNAL_NOT_FOUND) | ||
| error_code = _cconsts.NX_ERR_SIGNAL_NOT_FOUND | ||
| error_text = _errors.status_to_string(error_code) | ||
| raise errors.XnetError(error_text, error_code) |
nixnet/database/_lin_sched_entry.py
Outdated
| _errors.check_for_error(_cconsts.NX_ERR_DATABASE_OBJECT_NOT_FOUND) | ||
| error_code = _cconsts.NX_ERR_DATABASE_OBJECT_NOT_FOUND | ||
| error_text = _errors.status_to_string(error_code) | ||
| raise errors.XnetError(error_text, error_code) |
nixnet/database/_pdu.py
Outdated
| _errors.check_for_error(_cconsts.NX_ERR_SIGNAL_NOT_FOUND) | ||
| error_code = _cconsts.NX_ERR_SIGNAL_NOT_FOUND | ||
| error_text = _errors.status_to_string(error_code) | ||
| raise errors.XnetError(error_text, error_code) |
nixnet/database/_signal.py
Outdated
| _errors.check_for_error(_cconsts.NX_ERR_FRAME_NOT_FOUND) | ||
| error_code = _cconsts.NX_ERR_FRAME_NOT_FOUND | ||
| error_text = _errors.status_to_string(error_code) | ||
| raise errors.XnetError(error_text, error_code) |
We have been using check_for_error, which conditionally raises an error, in cases where we want to unconditionally raise an error. This confuses mypy into thinking there is a possible code path that doesn't produce a return value. I'm adding raise_xnet_error to unconditionally raise an error, which also resolve the mypy issue.
2175e60 to
e0b74ad
Compare
|
|
||
| def parse_lin_comm_bitfield(first, second): | ||
| # (int) -> types.CanComm | ||
| # type: (int, int) -> types.LinComm |
BREAKING CHANGE:
Before, we just returned the status code.
However, the user previously had no way to get the text for the status code.
Now, return the status code and text in a tuple.
toxsuccessfully runs, including unit tests and style checks (see CONTRIBUTING.md).