Skip to content

errno_from_exception: require int in args fallback#3661

Open
HrachShah wants to merge 1 commit into
tornadoweb:masterfrom
HrachShah:fix/errno-from-exception-int-fallback
Open

errno_from_exception: require int in args fallback#3661
HrachShah wants to merge 1 commit into
tornadoweb:masterfrom
HrachShah:fix/errno-from-exception-int-fallback

Conversation

@HrachShah

Copy link
Copy Markdown

Summary

errno_from_exception returns the exception's errno attribute when present, otherwise it falls back to args[0]. Every internal caller compares the result against an int errno constant (e.g. errno_from_exception(e) == errno.EBADF), so the args fallback has to return an int.

The pre-fix code returned args[0] as-is, which meant a str message — the shape used by most third-party OSError subclasses, by OSError subclasses that only set a human-readable message, and by any ValueError / RuntimeError raised with a string — flowed into the int-comparison call sites and silently turned every comparison into False. The real error class (and the right recovery branch) was hidden.

Fix

Pin the fallback to return args[0] only when it is an int, otherwise None — which matches the call sites' expectations and surfaces the "unknown errno" case the same way as the empty-args path.

Tests

Six new tests in tornado/test/util_test.py:ErrnoFromExceptionTest:

  • test_errno_attr_wins_over_args
  • test_args0_int_returned_when_no_errno_attr
  • test_empty_args_returns_none (the function used to raise TypeError here)
  • test_non_int_args0_returns_none
  • test_non_int_args0_with_int_args1_returns_none
  • test_message_only_args_returns_none

All 6 pass. The 6 new tests fail on the pre-fix code (with TypeError for the empty-args case and the wrong return value for the rest) and pass with the fix.

… args don't silently bypass the comparison

errno_from_exception has two branches: it returns the exception's errno
attribute when present, otherwise it falls back to args[0]. Every internal
caller compares the result against an int errno constant (e.g.
errno_from_exception(e) == errno.EBADF), so the args fallback has to
return an int.

The pre-fix code returned args[0] as-is, which meant a str message (the
shape used by most third-party OSError subclasses, by OSError subclasses
that only set a human-readable message, and by any value like ValueError
or RuntimeError raised with a string) flowed into the int-comparison
call sites and silently turned every comparison into False. The real
error class (and the right recovery branch) was hidden.

Pin the fallback to return args[0] only when it is an int, otherwise
None -- which matches the call sites' expectations and surfaces the
'unknown errno' case the same way as the empty-args path. Add six
tests in tornado/test/util_test.py:ErrnoFromExceptionTest covering the
errno attribute winning over args, an int args[0] being returned, an
empty args tuple returning None (the function used to raise
TypeError), a non-int args[0] returning None, an int args[1] not being
picked up when args[0] is a message, and a message-only exception
returning None.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant