GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — errno and strerror attributes incorrectly set on socket errors wrapped by urllib · Issue #50720 · python/cpython

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

New issue
Copy link
New issue
Copy link
Closed
Closed
errno and strerror attributes incorrectly set on socket errors wrapped by urllib#50720
Copy link
Assignees
orsenthil
Labels
3.16new features, bugs and security fixesnew features, bugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@ezio-melotti
ezio-melotti
opened on Jul 12, 2009
Issue body actions
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']

    Linked PRs

    • gh-50720: Set errno and strerror on URLError wrapping an OSError #160938
    • gh-50720: Copy winerror to URLError on Windows #161047

    Activity

    Next
    1. ezio-melotti commented on Jul 12, 2009

      @ezio-melotti
      ezio-melotti
      on Jul 12, 2009
      MemberAuthor
      More actions

      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.)

    2. ezio-melotti
      assigned
      birkenfeld
      on Jul 12, 2009
    3. ezio-melotti
      added
      docsDocumentation in the Doc dirDocumentation in the Doc dir
      stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
      type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
      on Jul 12, 2009
    4. amauryfa commented on Jul 12, 2009

      @amauryfa
      amauryfa
      on Jul 12, 2009
      Contributor
      More actions

      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.

    5. amauryfa
      removed
      docsDocumentation in the Doc dirDocumentation in the Doc dir
      on Jul 12, 2009
    6. amauryfa
      unassigned
      birkenfeld
      on Jul 12, 2009
    7. bitdancer commented on Jul 13, 2009

      @bitdancer
      bitdancer
      on Jul 13, 2009
      Member
      More actions

      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.

    8. 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[/+] on Jul 13, 2009
    9. ezio-melotti commented on Feb 27, 2010

      @ezio-melotti
      ezio-melotti
      on Feb 27, 2010
      MemberAuthor
      More actions

      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

    10. catherine commented on Jul 29, 2012

      @catherine
      catherinemannequin
      on Jul 29, 2012
      Mannequin
      More actions

      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.

    11. bitdancer commented on Aug 3, 2012

      @bitdancer
      bitdancer
      on Aug 3, 2012
      Member
      More actions

      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.

    12. ezio-melotti commented on Sep 19, 2012

      @ezio-melotti
      ezio-melotti
      on Sep 19, 2012
      MemberAuthor
      More actions

      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')

    13. bitdancer commented on Sep 19, 2012

      @bitdancer
      bitdancer
      on Sep 19, 2012
      Member
      More actions

      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.

    14. 9 remaining items

    15. vasiliyk commented on Oct 21, 2026

      @vasiliyk
      vasiliyk
      on Oct 21, 2026
      Contributor
      More actions

      Still reproduces on 3.16.0a1+dev:

      >>> 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

      URLError.__init__ sets only args and reason, so the OSError attributes are never set.

      Following the original patch:

      • in URLError.__init__, if reason is an OSError, copy its errno and strerror.
      • args, str() and repr() do not change, so the backward-compatibility concern does not apply.

      I can open a PR with tests if this approach is fine.

    16. orsenthil commented on Oct 29, 2026

      @orsenthil
      orsenthil
      on Oct 29, 2026
      Member
      More actions

      @vasiliyk, please create the patch with the approach.

      Reacted by Vasiliy Kiryanov
    17. vasiliyk commented on Oct 30, 2026

      @vasiliyk
      vasiliyk
      on Oct 30, 2026
      Contributor
      More actions

      @orsenthil The PR is up - #160938. I added a a versionchanged note for 3.16 in urllib.error doc.

    18. orsenthil
      moved this from Todo to In Progress in Urllib issueson Oct 31, 2026
    19. orsenthil
      added a commit that references this issue on Oct 31, 2026

      Merge branch 'main' into pythongh-50720-urllib

      e10e451
    20. orsenthil
      added
      3.16new features, bugs and security fixesnew features, bugs and security fixes
      and removed
      3.11only security fixesonly security fixes
      3.10 (EOL)end of lifeend of life
      3.12only security fixesonly security fixes
      on Oct 31, 2026
    21. orsenthil commented on Oct 31, 2026

      @orsenthil
      orsenthil
      on Oct 31, 2026
      Member
      More actions

      Thanks for this patch. This shouldn't be backported as there is subtle scenario wherein if an old code was checking e.errno is None previously to fall back on OSError, it will break and will need a change.

    22. orsenthil
      moved this from In Progress to Done in Urllib issueson Oct 31, 2026
    23. orsenthil
      closed this as completedby moving to Done in Urllib issueson Oct 31, 2026
    24. orsenthil
      added 2 commits that reference this issue on Oct 31, 2026

      gh-50720: Set errno and strerror on URLError wrapping an OSError (#16…

      2cf2dd6

      gh-50720: Copy winerror to URLError on Windows (#161047)

      0fd00ee
    25. orsenthil
      self-assigned this
      on Nov 10, 2026
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    • @orsenthil
      orsenthil

    Labels

    3.16new features, bugs and security fixesnew features, bugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    • Urllib issues
      Status
      Done
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.