I've been trying to find a way to completely exit/skip my with BaseObject.ethernet_device(ip_addr) as obj:
code block if there are errors in the __enter__
function.
In the example below this would be if the device is not present on the network. Any solutions would hopefully go in place of the line return None
(which I know is not what's needed).
class BaseObject(object):
@class_method
def ethernet_device(cls, host:str):
return EthernetDevice(host)
def __enter__(self):
return self
def __exit__(self, *args):
self._inst.close()
class EthernetDevice(BaseObject):
def __init__(self, host: str):
self._host = host
def __enter__(self):
try:
socket.inet_ntoa(self._host)
ip_addr = self._host
except:
logger.debug(f'{self._host} is not a valid IP address: trying as a hostname')
try:
logger.debug(f"Trying to resolve host {self._host}")
ip_addr = socket.gethostbyname(self._host)
except socket.gaierror:
logger.error(f"Couldn't resolve host {self._host}")
return None
self._inst = vxi11.Instrument(ip_addr)
return super().__enter__()
Usage:
with BaseObject.ethernet_device(ip_addr) as device:
print(device.id)
CodePudding user response:
The only answer here is to raise an exception inside __enter__
; __exit__
will always run if __enter__
exits without an exception (assuming you don't manage to segfault the interpreter or call os._exit
to intentionally kill the program without running cleanup blocks).
This will put it on the caller to catch the exception (wrapping the with
with a try
/except
) to prevent it bubbling through the rest of the program.
If the caller doesn't like indenting the whole block for this, they can narrow the scope using an ExitStack
, but that's the only "improvement" available, and it still requires __enter__
to raise an exception:
with contextlib.ExitStack() as stack:
try:
device = stack.enter_context(BaseObject.ethernet_device(ip_addr)
except CustomException:
return # Or do something else to skip remaining code
# Rest of code using device
# device is cleaned up for you if it exists