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

errno and strerror attributes incorrectly set on socket errors wrapped by urllib #50720

Open
ezio-melotti opened this issue Jul 12, 2009 · 9 comments
Labels
3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@ezio-melotti
Copy link
Member

BPO 6471
Nosy @birkenfeld, @amauryfa, @orsenthil, @ezio-melotti, @bitdancer, @cjerdonek, @iritkatriel
Files
  • keeperrdata.patch: patch to retain errno and strerror
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2009-07-12.20:54:33.896>
    labels = ['type-bug', 'library', '3.9', '3.10', '3.11']
    title = 'errno and strerror attributes incorrectly set on socket errors wrapped by urllib'
    updated_at = <Date 2021-11-30.20:20:38.331>
    user = 'https://github.com/ezio-melotti'

    bugs.python.org fields:

    activity = <Date 2021-11-30.20:20:38.331>
    actor = 'iritkatriel'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2009-07-12.20:54:33.896>
    creator = 'ezio.melotti'
    dependencies = []
    files = ['26559']
    hgrepos = []
    issue_num = 6471
    keywords = ['patch']
    message_count = 9.0
    messages = ['90458', '90462', '90466', '100178', '166705', '167330', '170710', '170723', '407399']
    nosy_count = 9.0
    nosy_names = ['georg.brandl', 'amaury.forgeotdarc', 'orsenthil', 'ezio.melotti', 'r.david.murray', 'chris.jerdonek', 'catherine', 'denkoren', 'iritkatriel']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = 'needs patch'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue6471'
    versions = ['Python 3.9', 'Python 3.10', 'Python 3.11']

    @ezio-melotti
    Copy link
    Member Author

    In Python 2.6, socket.error was changed to be a child class of IOError
    1. IOError derives from EnvironmentError 2, and EnvironmentError
    accepts a 2-tuple used to set the values of the errno and strerror
    attributes respectively 3.
    Apparently the IOError raised by the socket module are instantiated
    passing (always?) 'socket error' as first arg and an instance of
    socket.gaierror, socket.timeout or socket.herror (and maybe others) as
    second arg.
    The errno attributes ends up being a string (and not a number) and the
    strerror another exception (and not a str):

    >>> import socket
    >>> from urllib import urlopen
    >>> socket.setdefaulttimeout(0.01)
    >>> try: urlopen('http://www.python.org')
    ... except Exception, e: err1 = e
    ...
    >>> err1
    IOError('socket error', timeout('timed out',))
    >>> err1.errno
    'socket error'
    >>> err1.strerror
    timeout('timed out',)
    >>> err1.strerror.errno
    >>> err1.strerror.strerror
    >>>
    
    >>> try: urlopen('http://www.pythonfoobarbaz.org')
    ... except Exception, e: err2 = e
    ...
    >>> err2
    IOError('socket error', gaierror(11001, 'getaddrinfo failed'))
    >>> err1.errno
    'socket error'
    >>> err1.strerror
    timeout('timed out',)
    >>> err1.strerror.errno
    >>> err2.errno
    'socket error'
    >>> err2.strerror
    gaierror(11001, 'getaddrinfo failed')
    >>> err2.strerror.errno
    11001
    >>> err2.strerror.strerror
    'getaddrinfo failed'

    The 'socket error' strings doesn't provide any useful information
    (herror, gaierror and timeout are already subclasses of socket.error)
    and it results in confusing messages like:
    IOError: [Errno socket error] [Errno 11001] getaddrinfo failed

    The relevant information is not accessible directly on the error but it
    is in err.strerror/err.args1.

    IMHO the first arg should be the errno (if it's available) and the
    second the message (e.g. 'getaddrinfo failed' or 'timed out').

    The doc of socket.error 1 should be also changed because it says:
    "The accompanying value is either a string telling what went wrong or a
    pair (errno, string) representing an error returned by a system call,
    similar to the value accompanying os.error." (and this is actually what
    I'd like it to be, but right now it's something different.)

    @ezio-melotti ezio-melotti added docs Documentation in the Doc dir stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels Jul 12, 2009
    @amauryfa
    Copy link
    Member

    No, it seems that 2.5 has the same problem. The 'socket error' message is
    raised in urllib.py. The socket module is innocent to me...
    It appears that this file routinely raises IOErrors, passing various
    arguments, which are not stored properly in the IOError object. IMO it
    should raise subclasses of IOError.
    In this particular case of "socket error", it should let the exception
    propagate. Other occurrences need more thinking.

    @amauryfa amauryfa removed the docs Documentation in the Doc dir label Jul 12, 2009
    @bitdancer
    Copy link
    Member

    Yes, it looks to me like urllib is intentionally putting the 'socket
    error' or 'url error' into the errno position in the IOError arguments.
    Now that socket.error is an IOError, that at least seems wrong.

    @bitdancer bitdancer changed the title errno and strerror attributes incorrectly set on socket.error errno and strerror attributes incorrectly set on socket errors wrapped by urllib Jul 13, 2009
    @ezio-melotti
    Copy link
    Member Author

    Can this be fixed without breaking compatibility?
    It also affects Python2.7 and maybe also Python 3.x (there the error is different and might be intentional).

    Copy/pastable snippet to reproduce the error on 2.x:
    from urllib import urlopen
    try:
    urlopen('http://www.pythonfoobarbaz.org')
    except Exception, err:
    print 'err:', err
    print 'repr(err):', repr(err)
    print 'err.errno:', err.errno
    print 'err.strerror:', err.strerror
    print 'err.strerror.errno:', err.strerror.errno
    print 'err.strerror.strerror:', err.strerror.strerror

    Result on 2.7:
    err: [Errno socket error] [Errno -2] Name or service not known
    repr(err): IOError('socket error', gaierror(-2, 'Name or service not known'))
    err.errno: socket error
    err.strerror: [Errno -2] Name or service not known
    err.strerror.errno: -2
    err.strerror.strerror: Name or service not known

    Copy/pastable snippet to reproduce the error on 3.x:
    from urllib.request import urlopen
    try:
    urlopen('http://www.pythonfoobarbaz.org')
    except Exception as exc:
    err = exc
    print('err:', err)
    print('repr(err):', repr(err))
    print('err.errno:', err.errno)
    print('err.strerror:', err.strerror)
    print('err.reason:', err.reason)
    print('err.reason.errno:', err.reason.errno)
    print('err.reason.strerror:', err.reason.strerror)

    Result on 3.2:
    err: <urlopen error [Errno -2] Name or service not known>
    repr(err): URLError(gaierror(-2, 'Name or service not known'),)
    err.errno: None
    err.strerror: None
    err.reason: [Errno -2] Name or service not known
    err.reason.errno: -2
    err.reason.strerror: Name or service not known

    @catherine
    Copy link
    Mannequin

    catherine mannequin commented Jul 29, 2012

    It's very hard to tell what ought to be done here, since Lib/urllib/request.py throws URLErrors with a great variety of order and number of arguments, and it's not clear how URLError (in Lib/urllib/error.py) intends to handle them.

    However, in this case, AbstractHTTPHandler.do_open is instantiating URLError with another exception instance, and that exception contains .errno and .strerror. URLError puts the entire error instance into reason, where the information is hidden away as .reason.strno and .reason.strerror.

    In the name of keeping this information available rather than hiding it, I'm attaching a patch that adds to URLError.__init__:

            if hasattr(reason, "errno"):
                self.errno = reason.errno
            if hasattr(reason, "strerror"):
                self.strerror = reason.strerror

    Again, I'm not sure this is the most logical approach because I can't find a consistent pattern in the ways URLError is instantiated.

    @bitdancer
    Copy link
    Member

    This is an interesting idea and should at least improve matters. I'm wondering, though...I seem to remember writing code that fished the wrapped error out using one of those attributrs...but I'm not at a computer where I can try to check on that. Hopefully I can check on it this weekend.

    @ezio-melotti
    Copy link
    Member Author

    I seem to remember writing code that fished the wrapped error
    out using one of those attributrs...

    That would be err.reason:

    from urllib.request import urlopen
    try:
        urlopen('http://www.pythonfoobarbaz.org')
    except Exception as exc:
        print('err:', err)
        print('repr(err):', repr(err))
        print('err.reason:', err.reason)
        print('repr(err.reason):', repr(err.reason))

    prints:

    err: <urlopen error [Errno -2] Name or service not known>
    repr(err): URLError(gaierror(-2, 'Name or service not known'),)
    err.reason: [Errno -2] Name or service not known
    repr(err.reason): gaierror(-2, 'Name or service not known')

    @bitdancer
    Copy link
    Member

    Ah, of course. I should have reread the whole issue :)

    The backward compatibility is the big concern here. Regardless of what we do about that, we should at least fix this in 3.4.

    @iritkatriel
    Copy link
    Member

    Reproduced on 3.11:

    >>> from urllib.request import urlopen
    >>> try:
    ...     urlopen('http://www.pythonfoobarbaz.org')
    ... except Exception as exc:
    ...     err = exc
    ...     print('err:', err)
    ...     print('repr(err):', repr(err))
    ...     print('err.errno:', err.errno)
    ...     print('err.strerror:', err.strerror)
    ...     print('err.reason:', err.reason)
    ...     print('err.reason.errno:', err.reason.errno)
    ...     print('err.reason.strerror:', err.reason.strerror)
    ... 
    err: <urlopen error [Errno 8] nodename nor servname provided, or not known>
    repr(err): URLError(gaierror(8, 'nodename nor servname provided, or not known'))
    err.errno: None
    err.strerror: None
    err.reason: [Errno 8] nodename nor servname provided, or not known
    err.reason.errno: 8
    err.reason.strerror: nodename nor servname provided, or not known

    @iritkatriel iritkatriel added 3.9 only security fixes 3.10 only security fixes 3.11 only security fixes labels Nov 30, 2021
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @iritkatriel iritkatriel added 3.12 bugs and security fixes and removed 3.9 only security fixes labels Mar 1, 2023
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    5 participants