Best practices in Python for handling external exceptions (do I use raise?)
I'm writing a procedure in Python which at it's fundamental level communicates with a motor controller. It is possible for the controller to throw flags indicating that an error has occurred. I'm trying to figure how to best handle these errors.
In the example below, there are three possible errors, a temperature fault, a current limit fault and a voltage fault. I've handled them differently. Is there a correct way or is it subjective?
class motor_fault(Exception):
def __init__(self,error):
motor.move_at = 0 #Stop motor
self.error = error
def __str__(self):
return repr(self.value)
motor.velocity_limit = motor.slow
motor.velocity_limit_enable = True
try:
motor.move_to_absolute = motor.instrument_pos
while motor.in_position == 0:
if motor.current_limit == 1:
motor.move_at = 0 #Stop motor
print('Motor current error')
break
if motor.temp_fault == 1: raise motor_fault('Temperature Fault')
if motor.voltage_fault == 1: raise voltage_fault:
time.sleep(0.5)
else:
print('reached desired instrument position with no faults')
except motor_temp_fault as e:
#Not sure what I'd do here...
print('My exception occurred, value:', e.error)
pass
except:
motor.move_at = 0 #Stop motor just in case
print(' some other fault, probably voltage')
else:
print (' this is only printed if there were no errors')
finally:
print ('this is printed regardless of how the try exits')
It seems a lot simpler to drop the whole try:
. Just set a flag in the while loop and break. After the loop, look at the flag and see if the while loop exited successfully.
fault = False
while motor.in_position == 0:
if motor.current_limit == 1:
fault = 'Motor current error'
break
if motor.temp_fault == 1:
fault = 'Motor temperature error'
break
if motor.voltage_fault == 1:
fault = 'Motor voltage error'
开发者_高级运维break
time.sleep(0.5)
else:
print('reached waterline with no faults')
if fault:
motor.move_at = 0 #Stop motor
print(fault)
# Now look at the fault string to determine the next course of action.
But that somehow seems wrong or non-pythonic to use a term I don't really understand. Is there really anything wrong with this? Thanks and please keep in mind I'm not CS major and I haven't taken a programming class since 1982.
My approach, for what it's worth, would be to define a small hierarchy of exceptions, say:
class MotorFaultError(Exception) # top level exception
class MotorTempFault(MotorFaultError)
class MotorVoltageFault(MotorFaultError)
# etc
Then, on any error, make sure your API throws one of those. If your API, itself, has to catch an exception from the underlying motor API, wrap that exception in one of your own exceptions.
Rationale:
Your own exception hierarchy is part of the API and serves to isolate the calling code from the specifics of the underlying motor API. By throwing a defined set of exceptions, rather than allowing the motor API's exceptions bubble up, you further hide the underlying API. Doing so makes it easier to drop another motor API in place, for whatever reason, including:
- You found a better one.
- You want to do some testing with a mocked-up motor API.
Also, exceptions (rather than flags) are more consistent with the way other Python APIs behave.
I'd go for exceptions with many except clauses for all the different exceptions you would like to handle at this point as these cases seem to be exceptional/failure scenarios.
I would not use flags to represent these scenarios as it would add more fields to motor which dont seem to be useful/relevant outside of this use case.
As far as knowing if it is the 'correct' way to handle this well, if both solution works they are both correct!
Hope I was clear enough... ;-)
I don't see anything wrong with either approach. Personally, I prefer the try-except one, but it's just preference.
Is there a correct way
Yes.
or is it subjective?
No.
Use the raise
statement.
First, please use CapitalLetters for your unique exceptions
class Motor_Fault( Exception ): pass
class Temperature_Fault( Motor_Fault ): pass
class Voltage_Fault( Motor_Fault ): pass
class Current_Fault( Motor_Fault ): pass
Second, separate the error detection from the rest of your processing.
Third, don't do anything in the exception class. Handle the motor stop business in your application.
Fourth, The motor status check does not belong in your application's motor loop. This is all part of the method function that implements motor.move_to_absolute
.
if motor.current_limit == 1: raise Current_Fault()
if motor.temp_fault == 1: raise Temperature_Fault()
if motor.voltage_fault == 1: raise Voltage_Fault()
Fifth, your application loop should look like this.
motor.velocity_limit = motor.slow
motor.velocity_limit_enable = True
try:
motor.move_to_absolute = motor.instrument_pos
while motor.in_position == 0:
time.sleep(0.5)
print('reached desired instrument position with no faults')
except Motor_Fault, e:
motor.move_at = 0 #Stop motor
print(fault)
The motor should raise it's own exceptions. If, for some reason, it can't, then you can "wrap" the motor with some status checking. This isn't ideal, since the motor should raise it's own exceptions.
def check():
if motor.current_limit == 1: raise Current_Fault()
if motor.temp_fault == 1: raise Temperature_Fault()
if motor.voltage_fault == 1: raise Voltage_Fault()
Call this function just before sleep
.
精彩评论