requests icon indicating copy to clipboard operation
requests copied to clipboard

Preserve exception chains in HTTPAdapter with explicit 'from' clause

Open tboy1337 opened this issue 3 months ago • 1 comments

Summary

This PR enhances exception handling in the HTTPAdapter class by preserving exception chains using Python's explicit from clause. This change improves debugging and error traceability by maintaining the original exception context when wrapping or converting urllib3 exceptions to requests-specific exceptions.

Changes Made

Core Changes (src/requests/adapters.py)

Modified 13 exception raise statements across two key methods:

  1. get_connection_with_tls_context() - Preserves chain when ValueError is converted to InvalidURL
  2. send() - Preserves chains for the following exception conversions:
    • LocationValueErrorInvalidURL
    • ProtocolError / OSErrorConnectionError
    • MaxRetryError (with various reasons) → ConnectTimeout, RetryError, ProxyError, SSLError, or ConnectionError
    • ClosedPoolErrorConnectionError
    • _ProxyErrorProxyError
    • _SSLErrorSSLError
    • ReadTimeoutErrorReadTimeout
    • _InvalidHeaderInvalidHeader

Testing (tests/test_adapters.py)

Added comprehensive test coverage with 14 new test methods in a dedicated TestExceptionChaining class:

  • Each test verifies that the __cause__ attribute is properly set when exceptions are raised
  • Tests cover all modified exception handling paths
  • Uses mocking to simulate various urllib3 exception scenarios
  • Ensures backward compatibility while improving error context

Benefits

  1. Better Debugging: Developers can trace the full exception chain to understand the root cause of errors
  2. Improved Error Context: The original exception details are preserved rather than being lost during conversion
  3. Python Best Practices: Follows PEP 3134 guidelines for explicit exception chaining
  4. No Breaking Changes: The change is purely additive and doesn't affect the public API or existing exception behavior

Testing

All new exception chaining behavior is covered by unit tests that verify:

  • The correct exception type is raised
  • The exception chain is preserved via the __cause__ attribute
  • All 14 exception conversion scenarios maintain proper chaining

Example

Before:

raise ConnectionError(err, request=request)

After:

raise ConnectionError(err, request=request) from err

When an error occurs, users will now see the full exception chain in tracebacks, making it easier to diagnose issues.

tboy1337 avatar Oct 21 '25 12:10 tboy1337

I should add that this issue was found because of I think (I'm not 100% sure because everything was also typed according to mypy rules in the commit) pylint in https://github.com/psf/requests/pull/7054, if it was pylint that found this issue and it is a legitimate issue then it makes a strong case for the project to start using it imo.

tboy1337 avatar Oct 22 '25 12:10 tboy1337