mavutil: tidy exceptions - #1252
Conversation
| print(f"SSL Error: {e}") | ||
| self.close() | ||
| raise | ||
| except OSError as e: |
There was a problem hiding this comment.
just a reorder as oserror cover sslerror , so we need to handle them first
|
I do like the change, but there are several regressions. I suggest maybe subset the easier ones to get them in first.... I'm particularly concerned about the win32 implications. |
Replace handlers that say less than they mean, without changing behaviour. Most of this is a rename: socket.error and select.error are aliases of OSError in Python 3 - literally the same object - so spelling them OSError says the same thing in the spelling the language uses. Alongside that, `except X as e` where e is never referenced loses the binding, three `pass` statements trailing an if inside an except body are dropped as dead, and a handful of handlers are narrowed to what the guarded call can actually raise: - auto_mavlink_version()'s ord(buf[0]) raises TypeError when buf is bytes and buf[0] is therefore already an int; that is the whole reason for the fallback. - set_rtscts()/set_baudrate() fall back to attribute assignment because pySerial 3.0 dropped setRtsCts()/setBaudrate(); a missing method is AttributeError, and the comment already says so. - getsockname(), connect() and accept() raise OSError. - the curses import can only fail with ImportError. - the custom mode map's dict-building step raises ValueError from int(), or AttributeError/TypeError when the JSON is not shaped like a mapping. Its inner `except OSError: raise` is deleted: re-raising immediately is what not catching does, and the outer handler already covers it. Deliberately left broad, having been flagged in review as regressions when narrowed: - the optional top-level dialect import and set_dialect()'s regenerate fallback. Both are best-effort by intent - the latter self-heals a corrupt or partially written generated dialect, which fails with SyntaxError rather than ImportError. - set_close_on_exec(), whose docstring is "Ignore exceptions". fcntl raises TypeError for fd=None and ValueError for a negative fd, neither an OSError. - both fileno() probes and mavserial.write()/reset(). pySerial's win32 backend does not define fileno() and so inherits io.RawIOBase.fileno(), which raises io.UnsupportedOperation - an OSError and ValueError, but not a SerialException. FakeSerial.write() raises a bare Exception on purpose, so narrowing write() breaks the force_connected path, and reset() promises a bool rather than raising. Also note serial.SerialException already subclasses OSError, so (serial.SerialException, OSError) would have collapsed to OSError anyway. - auto_detect_serial_win32(), which must degrade to an empty list whatever pySerial's enumeration does. Verified with mypy and the test suite; 33 passed, 8 skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…achable
connect() listed its socket error handler before its ssl.SSLError handler:
except socket.error as e: # socket.error is OSError
if e.errno in [errno.ECONNREFUSED, errno.EHOSTUNREACH]:
self.close()
return
raise
except ssl.SSLError as e:
print(f"SSL Error: {e}")
self.close()
raise
ssl.SSLError subclasses OSError, so the first clause always won and the second
was dead code. An SSL failure fell into the errno test, matched neither
ECONNREFUSED nor EHOSTUNREACH, and reached the bare raise - propagating without
the diagnostic and, more importantly, without closing the socket, leaking it on
every failed handshake.
Order the specific handler first. A failed handshake now prints and closes before
re-raising, which is what the code was written to do.
The other three try blocks in this class already have ssl.SSLError first and are
unaffected. Their ordering is what makes the bug in connect() easy to miss.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cb04a95 to
ce49f2e
Compare
|
@peterbarker I tried to keep the one's that shouldn't be blocking. Other may need so other change so part of another PR |
peterbarker
left a comment
There was a problem hiding this comment.
I don't think I will be able to approve/merge these, I'm afraid, I think tridge will need to do that.
We would probably need to do an audit of MAVProxy to make sure that its resilience is not impacted by this. Changing the API from "catch exception, return value" to what it should have been (if you can't handle an exception then don't look for the problem) is not a small change to be making!
| AP_MAV_TYPE_MODE_MAP = AP_MAV_TYPE_MODE_MAP_DEFAULT.copy() | ||
| AP_MAV_TYPE_MODE_MAP.update(_custom_mode_map) | ||
| except Exception: | ||
| except (OSError, ValueError, AttributeError, TypeError, json.decoder.JSONDecodeError): |
There was a problem hiding this comment.
Please revert this one. You're missing at least RecursionError
I'd actually like to consider removing the entire try/except block here on the basis that the mode mapping file is relatively static and the user probably does want an early-exit with mavutil.py is imported!
| try: | ||
| self.port.setRtsCts(enable) | ||
| except Exception: | ||
| except AttributeError: |
There was a problem hiding this comment.
| except AttributeError: | |
| except (AttributeError, NotImplementedError): |
tidy broad exceptions
remove useless pass
select.error is alias OSError : use OSError to make analyzer happy