Catch low-level python exceptions, instead re-raising urllib3 variants, so that low-level exceptions are not leaked in the high-level api. On exit, release the connection back to the pool.
(self)
| 891 | |
| 892 | @contextmanager |
| 893 | def _error_catcher(self) -> typing.Generator[None]: |
| 894 | """ |
| 895 | Catch low-level python exceptions, instead re-raising urllib3 |
| 896 | variants, so that low-level exceptions are not leaked in the |
| 897 | high-level api. |
| 898 | |
| 899 | On exit, release the connection back to the pool. |
| 900 | """ |
| 901 | clean_exit = False |
| 902 | |
| 903 | try: |
| 904 | try: |
| 905 | yield |
| 906 | |
| 907 | except SocketTimeout as e: |
| 908 | # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but |
| 909 | # there is yet no clean way to get at it from this context. |
| 910 | raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type] |
| 911 | |
| 912 | except BaseSSLError as e: |
| 913 | # SSL errors related to framing/MAC get wrapped and reraised here |
| 914 | raise SSLError(e) from e |
| 915 | |
| 916 | except IncompleteRead as e: |
| 917 | if ( |
| 918 | e.expected is not None |
| 919 | and e.partial is not None |
| 920 | and e.expected == -e.partial |
| 921 | ): |
| 922 | arg = "Response may not contain content." |
| 923 | else: |
| 924 | arg = f"Connection broken: {e!r}" |
| 925 | raise ProtocolError(arg, e) from e |
| 926 | |
| 927 | except (HTTPException, OSError) as e: |
| 928 | raise ProtocolError(f"Connection broken: {e!r}", e) from e |
| 929 | |
| 930 | # If no exception is thrown, we should avoid cleaning up |
| 931 | # unnecessarily. |
| 932 | clean_exit = True |
| 933 | finally: |
| 934 | # If we didn't terminate cleanly, we need to throw away our |
| 935 | # connection. |
| 936 | if not clean_exit: |
| 937 | # The response may not be closed but we're not going to use it |
| 938 | # anymore so close it now to ensure that the connection is |
| 939 | # released back to the pool. |
| 940 | if self._original_response: |
| 941 | self._original_response.close() |
| 942 | |
| 943 | # Closing the response may not actually be sufficient to close |
| 944 | # everything, so if we have a hold of the connection close that |
| 945 | # too. |
| 946 | if self._connection: |
| 947 | self._connection.close() |
| 948 | |
| 949 | # If we hold the original response but it's closed now, we should |
| 950 | # return the connection back to the pool. |
no test coverage detected