1

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?

6
  • 8
    Extract the code into a function. Then you can return early instead of nesting or using conditionals.
    – amon
    Nov 10, 2021 at 0:01
  • @amon are you referring to the content inside the else block? The results of the try-except blocks there potentially alter flags in the local scope that are needed below; so how would extracting into a method and calling it work when I cannot pass the flags and alter their state in CPython?
    – pstatix
    Nov 10, 2021 at 1:32
  • Yes, the lack of pointer-like data structures is a severe limitation in Python. I'd have extracted the entire shown code into a function, then 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 and reg_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 from ConnectRegistry() despite being unable to continue.
    – amon
    Nov 10, 2021 at 12:55
  • 1
    IMO using boolean flags in this way is a code smell. What purpose do they serve exactly here? Do they tell you something distinct from reg_a is not None?
    – JimmyJames
    Nov 10, 2021 at 18:43
  • 1
    @amon "the lack of pointer-like data structures is a severe limitation in Python" Really? I've never needed that feature in more than 20 years of coding professionally.
    – JimmyJames
    Nov 10, 2021 at 19:01

2 Answers 2

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
1
  • this - the control flow in the OP is correct, it's just verbose. The solution is to solve the verbosity
    – user253751
    Nov 11, 2021 at 10:04
1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.