Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Could ProxyTimeoutError inherit TimeoutError? #39

Closed
aaugustin opened this issue Jan 28, 2025 · 2 comments
Closed

Could ProxyTimeoutError inherit TimeoutError? #39

aaugustin opened this issue Jan 28, 2025 · 2 comments

Comments

@aaugustin
Copy link
Contributor

I noticed that ProxyConnectionError inherits OSError.

It would help me if ProxyTimeoutError followed the same pattern and inherited TimeoutError here.

Why it's a good idea in general

You're wrapping socket.timeout (which is TimeoutError on Python ≥ 3.10) and asyncio.TimeoutError (which is TimeoutError on Python ≥ 3.11) in a ProxyTimeoutError which adds details about the error.

I believe that wrapping in a subclass of the original exception is a good practice because catching the class of the original exception still works. Here's what I mean:

class Proxy:
    def connect():
        try:
            ...
        except TimeoutError as exc:
            raise ProxyTimeoutError("...") from exc

try:
    Proxy.connect()
except TimeoutError:  # this works only if ProxyTimeoutError inherits TimeoutError
    ...

Is it backwards-compatible?

Even though python-socks always wraps TimeoutError in ProxyTimeoutError, it's still possible to construct a scenario where this change would be backwards-incompatible:

try:
    do_something_that_can_raise_timeout_error()
    Proxy.connect()
except TimeoutError:
    # this branch would be taken after the change
    ...
except ProxyTimeoutError:
    # this branch was taken after the change
    ...

In my opinion, that pattern is fairly unlikely and it should be written as follows, with narrow exception catching:

try:
    do_something_that_can_raise_timeout_error()
except TimeoutError:
    ...
try:
    Proxy.connect()
except ProxyTimeoutError:
    ...

For this reason, I believe that the backwards compatibility concerns are bearable.

Why would it help me specifically

I just added support for SOCKS proxies in websockets with python-socks (python-websockets/websockets#1582). The library works well, thank you!

I'm now polishing error handling. websockets already contains code to retry automatically on connection errors and timeouts. Since ProxyConnectionError inherits OSError, automatic retries on ProxyConnectionError are already working without any change.

If ProxyTimeoutError inherited TimeoutError, it would work out of the box too. (Well, almost — I'd have to deal with asyncio.TimeoutError and TimeoutError being different classes in Python < 3.11.)

python-socks is an optional dependency of websockets because it may also be used for writing servers and there's no reason to install python-socks in that case. As a consequence, I cannot simply import ProxyTimeoutError in the code that handles retries; I need conditional imports; that creates complications.

If you don't make that change, I will rewrap ProxyTimeoutError into a standard TimeoutError... but wrapping back in the original exception class feels wrong... hence I'm suggesting this change.

@romis2012
Copy link
Owner

It seems reasonable. Fixed in v2.7.0

@aaugustin
Copy link
Contributor Author

That was fast! Thank you so much!

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

No branches or pull requests

2 participants