|
1 | | -""" |
2 | | -There are several specific :class:`Exception` classes to allow user |
3 | | -code to react to specific scenarios related to CAN busses:: |
4 | | -
|
5 | | - Exception (Python standard library) |
6 | | - +-- ... |
7 | | - +-- CanError (python-can) |
8 | | - +-- CanInterfaceNotImplementedError |
9 | | - +-- CanInitializationError |
10 | | - +-- CanOperationError |
11 | | - +-- CanTimeoutError |
12 | | -
|
13 | | -Keep in mind that some functions and methods may raise different exceptions. |
14 | | -For example, validating typical arguments and parameters might result in a |
15 | | -:class:`ValueError`. This should always be documented for the function at hand. |
16 | | -""" |
17 | | - |
18 | | -import sys |
19 | | -from contextlib import contextmanager |
20 | | - |
21 | | -from typing import Optional |
22 | | -from typing import Type |
23 | | - |
24 | | -if sys.version_info >= (3, 9): |
25 | | - from collections.abc import Generator |
26 | | -else: |
27 | | - from typing import Generator |
28 | | - |
29 | | - |
30 | | -class CanError(Exception): |
31 | | - """Base class for all CAN related exceptions. |
32 | | -
|
33 | | - If specified, the error code is automatically appended to the message: |
34 | | -
|
35 | | - >>> # With an error code (it also works with a specific error): |
36 | | - >>> error = CanOperationError(message="Failed to do the thing", error_code=42) |
37 | | - >>> str(error) |
38 | | - 'Failed to do the thing [Error Code 42]' |
39 | | - >>> |
40 | | - >>> # Missing the error code: |
41 | | - >>> plain_error = CanError(message="Something went wrong ...") |
42 | | - >>> str(plain_error) |
43 | | - 'Something went wrong ...' |
44 | | -
|
45 | | - :param error_code: |
46 | | - An optional error code to narrow down the cause of the fault |
47 | | -
|
48 | | - :arg error_code: |
49 | | - An optional error code to narrow down the cause of the fault |
50 | | - """ |
51 | | - |
52 | | - def __init__( |
53 | | - self, |
54 | | - message: str = "", |
55 | | - error_code: Optional[int] = None, |
56 | | - ) -> None: |
57 | | - self.error_code = error_code |
58 | | - super().__init__( |
59 | | - message if error_code is None else f"{message} [Error Code {error_code}]" |
60 | | - ) |
61 | | - |
62 | | - |
63 | | -class CanInterfaceNotImplementedError(CanError, NotImplementedError): |
64 | | - """Indicates that the interface is not supported on the current platform. |
65 | | -
|
66 | | - Example scenarios: |
67 | | - - No interface with that name exists |
68 | | - - The interface is unsupported on the current operating system or interpreter |
69 | | - - The driver could not be found or has the wrong version |
70 | | - """ |
71 | | - |
72 | | - |
73 | | -class CanInitializationError(CanError): |
74 | | - """Indicates an error the occurred while initializing a :class:`can.BusABC`. |
75 | | -
|
76 | | - If initialization fails due to a driver or platform missing/being unsupported, |
77 | | - a :exc:`~can.exceptions.CanInterfaceNotImplementedError` is raised instead. |
78 | | - If initialization fails due to a value being out of range, a :class:`ValueError` |
79 | | - is raised. |
80 | | -
|
81 | | - Example scenarios: |
82 | | - - Try to open a non-existent device and/or channel |
83 | | - - Try to use an invalid setting, which is ok by value, but not ok for the interface |
84 | | - - The device or other resources are already used |
85 | | - """ |
86 | | - |
87 | | - |
88 | | -class CanOperationError(CanError): |
89 | | - """Indicates an error while in operation. |
90 | | -
|
91 | | - Example scenarios: |
92 | | - - A call to a library function results in an unexpected return value |
93 | | - - An invalid message was received |
94 | | - - The driver rejected a message that was meant to be sent |
95 | | - - Cyclic redundancy check (CRC) failed |
96 | | - - A message remained unacknowledged |
97 | | - - A buffer is full |
98 | | - """ |
99 | | - |
100 | | - |
101 | | -class CanTimeoutError(CanError, TimeoutError): |
102 | | - """Indicates the timeout of an operation. |
103 | | -
|
104 | | - Example scenarios: |
105 | | - - Some message could not be sent after the timeout elapsed |
106 | | - - No message was read within the given time |
107 | | - """ |
108 | | - |
109 | | - |
110 | | -@contextmanager |
111 | | -def error_check( |
112 | | - error_message: Optional[str] = None, |
113 | | - exception_type: Type[CanError] = CanOperationError, |
114 | | -) -> Generator[None, None, None]: |
115 | | - """Catches any exceptions and turns them into the new type while preserving the stack trace.""" |
116 | | - try: |
117 | | - yield |
118 | | - except Exception as error: # pylint: disable=broad-except |
119 | | - if error_message is None: |
120 | | - raise exception_type(str(error)) from error |
121 | | - else: |
122 | | - raise exception_type(error_message) from error |
| 1 | +""" |
| 2 | +There are several specific :class:`Exception` classes to allow user |
| 3 | +code to react to specific scenarios related to CAN busses:: |
| 4 | +
|
| 5 | + Exception (Python standard library) |
| 6 | + +-- ... |
| 7 | + +-- CanError (python-can) |
| 8 | + +-- CanInterfaceNotImplementedError |
| 9 | + +-- CanInitializationError |
| 10 | + +-- CanOperationError |
| 11 | + +-- CanTimeoutError |
| 12 | +
|
| 13 | +Keep in mind that some functions and methods may raise different exceptions. |
| 14 | +For example, validating typical arguments and parameters might result in a |
| 15 | +:class:`ValueError`. This should always be documented for the function at hand. |
| 16 | +""" |
| 17 | + |
| 18 | +import sys |
| 19 | +from contextlib import contextmanager |
| 20 | + |
| 21 | +from typing import Optional |
| 22 | +from typing import Type |
| 23 | + |
| 24 | +if sys.version_info >= (3, 9): |
| 25 | + from collections.abc import Generator |
| 26 | +else: |
| 27 | + from typing import Generator |
| 28 | + |
| 29 | + |
| 30 | +class CanError(Exception): |
| 31 | + """Base class for all CAN related exceptions. |
| 32 | +
|
| 33 | + If specified, the error code is automatically appended to the message: |
| 34 | +
|
| 35 | + >>> # With an error code (it also works with a specific error): |
| 36 | + >>> error = CanOperationError(message="Failed to do the thing", error_code=42) |
| 37 | + >>> str(error) |
| 38 | + 'Failed to do the thing [Error Code 42]' |
| 39 | + >>> |
| 40 | + >>> # Missing the error code: |
| 41 | + >>> plain_error = CanError(message="Something went wrong ...") |
| 42 | + >>> str(plain_error) |
| 43 | + 'Something went wrong ...' |
| 44 | +
|
| 45 | + :param error_code: |
| 46 | + An optional error code to narrow down the cause of the fault |
| 47 | +
|
| 48 | + :arg error_code: |
| 49 | + An optional error code to narrow down the cause of the fault |
| 50 | + """ |
| 51 | + |
| 52 | + def __init__( |
| 53 | + self, |
| 54 | + message: str = "", |
| 55 | + error_code: Optional[int] = None, |
| 56 | + ) -> None: |
| 57 | + self.error_code = error_code |
| 58 | + super().__init__( |
| 59 | + message if error_code is None else f"{message} [Error Code {error_code}]" |
| 60 | + ) |
| 61 | + |
| 62 | + |
| 63 | +class CanInterfaceNotImplementedError(CanError, NotImplementedError): |
| 64 | + """Indicates that the interface is not supported on the current platform. |
| 65 | +
|
| 66 | + Example scenarios: |
| 67 | + - No interface with that name exists |
| 68 | + - The interface is unsupported on the current operating system or interpreter |
| 69 | + - The driver could not be found or has the wrong version |
| 70 | + """ |
| 71 | + |
| 72 | + |
| 73 | +class CanInitializationError(CanError): |
| 74 | + """Indicates an error the occurred while initializing a :class:`can.BusABC`. |
| 75 | +
|
| 76 | + If initialization fails due to a driver or platform missing/being unsupported, |
| 77 | + a :exc:`~can.exceptions.CanInterfaceNotImplementedError` is raised instead. |
| 78 | + If initialization fails due to a value being out of range, a :class:`ValueError` |
| 79 | + is raised. |
| 80 | +
|
| 81 | + Example scenarios: |
| 82 | + - Try to open a non-existent device and/or channel |
| 83 | + - Try to use an invalid setting, which is ok by value, but not ok for the interface |
| 84 | + - The device or other resources are already used |
| 85 | + """ |
| 86 | + |
| 87 | + |
| 88 | +class CanOperationError(CanError): |
| 89 | + """Indicates an error while in operation. |
| 90 | +
|
| 91 | + Example scenarios: |
| 92 | + - A call to a library function results in an unexpected return value |
| 93 | + - An invalid message was received |
| 94 | + - The driver rejected a message that was meant to be sent |
| 95 | + - Cyclic redundancy check (CRC) failed |
| 96 | + - A message remained unacknowledged |
| 97 | + - A buffer is full |
| 98 | + """ |
| 99 | + |
| 100 | + |
| 101 | +class CanTimeoutError(CanError, TimeoutError): |
| 102 | + """Indicates the timeout of an operation. |
| 103 | +
|
| 104 | + Example scenarios: |
| 105 | + - Some message could not be sent after the timeout elapsed |
| 106 | + - No message was read within the given time |
| 107 | + """ |
| 108 | + |
| 109 | + |
| 110 | +@contextmanager |
| 111 | +def error_check( |
| 112 | + error_message: Optional[str] = None, |
| 113 | + exception_type: Type[CanError] = CanOperationError, |
| 114 | +) -> Generator[None, None, None]: |
| 115 | + """Catches any exceptions and turns them into the new type while preserving the stack trace.""" |
| 116 | + try: |
| 117 | + yield |
| 118 | + except Exception as error: # pylint: disable=broad-except |
| 119 | + if error_message is None: |
| 120 | + raise exception_type(str(error)) from error |
| 121 | + else: |
| 122 | + raise exception_type(error_message) from error |
0 commit comments