Ive got a boot sequence that needs to check some registry values, they may or may not be present, so each check needs to be wrapped in its own try-except. I try to avoid nesting as I think it can lead to confusion, but what I ended up with was:
reg_a = reg_b = True
reg_a_val = reg_b_val = None
try:
hklm = ConnectRegistry(None, HKLM)
except OSError as err:
# log unable to connect to hivekey
else:
try:
reg_a_val = _get_a_val(hklm)
except OSError as err:
# log failure
reg_a = False
try:
reg_b_val = _get_b_val(hklm)
except OSError as err:
# log failure
reg_b = False
In an attempt to avoid this, I thought maybe setting a flag prior would reduce:
hklm = None
try:
hklm = ConnectRegistry(None, HKLM)
except OSError as err:
# log unable to connect to hivekey
if hklm:
try:
reg_a_val = _get_a_val(hklm)
except OSError as err:
# log failure
reg_a = False
try:
reg_b_val = _get_b_val(hklm)
except OSError as err:
# log failure
reg_b = False
But I then realized its the same depth as just putting them in the else
block.
So which would be preferred? If neither, is there an alternative when subsequent try-except blocks must be used while accessing a variable from a preceding try-except?
Edit:
Another form would be to exclude the else block and roll the nesting into the try-except, essentially implying an "else" should ConnectRegistry
not fail:
reg_a_val = reg_b_val = None
try:
with ConnectRegistry(None, HKLM) as hklm:
try:
reg_a_val = _get_a_val(hklm)
except OSError as err:
# log failure
try:
reg_b_val = _get_b_val(hklm)
except OSError as err:
# log failure
except OSError as err:
# log unable to connect to hivekey
This is done because the outer try-except would need to log failure to even access the hiveroot, whereas the inner try-excepts may or may not fail, but we need to continue even if one does fail. Therefore, the question becomes, is nesting a try-except always considered bad practice, or is a case like this perfectly clear?
2 Answers 2
From what I can tell, this is equivalent to what you are doing:
def reg_get(func, hklm):
try:
return True, func(hklm)
except OSError as err:
# log failure
return False, None
reg_a = reg_b = True
reg_a_val = reg_b_val = None
try:
hklm = ConnectRegistry(None, HKLM)
reg_a, reg_a_val = reg_get(_get_a_val, hklm)
reg_b, reg_a_val = reg_get(_get_b_val, hklm)
except OSError as err:
# log unable to connect to hivekey
...
AS I noted in the comments, I don't get the point of the reg_a
and reg_b
though. They seem to be equivalent to reg_x_value is not None
. The only reason it wouldn't be equivalent is if these methods can return None on a successful call.
If you just don't want to repeat that multiple times (which is another code smell) you could simply do this at some point after you have done the calls.
reg_a = reg_a_value is not None
-
this - the control flow in the OP is correct, it's just verbose. The solution is to solve the verbosityStack Exchange Broke The Law– Stack Exchange Broke The Law2021年11月11日 10:04:29 +00:00Commented Nov 11, 2021 at 10:04
In a situation like this, I often find it useful to create one or more new exception classes, which I can then raise
manually. So, no matter how convoluted the "inner logic" might be, it all comes down to a specific set of (user-defined ...) exceptions that might be raised.
When each of the inner-level routines finds a situation from which it cannot plausibly continue, it does the same thing: it raises an exception of a particular class. Meanwhile, the outer-level logic is waiting for one of these exception classes to be raised.
reg_a, reg_b = get_reg_a_and_b()
. I know this doesn't quite work because you represent the state of each value via two variables (reg_a
andreg_a_val
) but it can be expanded as necessary. In some cases, the solution could be to keep the various flags in an object, instead of using variables in the local scope. The "real" problem is that you're swallowing the exception fromConnectRegistry()
despite being unable to continue.reg_a is not None
?