Skip to content

Commit 8bc0903

Browse files
committed
Improve transient_internet() again to detect more network errors,
and use it in test_robotparser. Fixes #8574.
1 parent 4b92b5f commit 8bc0903

File tree

2 files changed

+39
-22
lines changed

2 files changed

+39
-22
lines changed

Lib/test/support.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -787,8 +787,18 @@ def __exit__(self, type_=None, value=None, traceback=None):
787787
def transient_internet(resource_name, *, timeout=30.0, errnos=()):
788788
"""Return a context manager that raises ResourceDenied when various issues
789789
with the Internet connection manifest themselves as exceptions."""
790+
default_errnos = [
791+
('ECONNREFUSED', 111),
792+
('ECONNRESET', 104),
793+
('ENETUNREACH', 101),
794+
('ETIMEDOUT', 110),
795+
]
796+
790797
denied = ResourceDenied("Resource '%s' is not available" % resource_name)
791-
captured_errnos = errnos or (errno.ETIMEDOUT, errno.ECONNRESET)
798+
captured_errnos = errnos
799+
if not captured_errnos:
800+
captured_errnos = [getattr(errno, name, num)
801+
for (name, num) in default_errnos]
792802

793803
def filter_error(err):
794804
if (isinstance(err, socket.timeout) or
@@ -803,14 +813,20 @@ def filter_error(err):
803813
socket.setdefaulttimeout(timeout)
804814
yield
805815
except IOError as err:
806-
# socket.error inherits IOError
816+
# urllib can wrap original socket errors multiple times (!), we must
817+
# unwrap to get at the original error.
818+
while True:
819+
a = err.args
820+
if len(a) >= 1 and isinstance(a[0], IOError):
821+
err = a[0]
822+
# The error can also be wrapped as args[1]:
823+
# except socket.error as msg:
824+
# raise IOError('socket error', msg).with_traceback(sys.exc_info()[2])
825+
elif len(a) >= 2 and isinstance(a[1], IOError):
826+
err = a[1]
827+
else:
828+
break
807829
filter_error(err)
808-
# urllib.request wraps the original socket.error with IOerror:
809-
#
810-
# except socket.error as msg:
811-
# raise IOError('socket error', msg).with_traceback(sys.exc_info()[2])
812-
if len(err.args) >= 2 and isinstance(err.args[1], socket.error):
813-
filter_error(err.args[1])
814830
raise
815831
# XXX should we catch generic exceptions and look for their
816832
# __cause__ or __context__?

Lib/test/test_robotparser.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,23 +235,24 @@ class NetworkTestCase(unittest.TestCase):
235235

236236
def testPasswordProtectedSite(self):
237237
support.requires('network')
238-
# XXX it depends on an external resource which could be unavailable
239-
url = 'http://mueblesmoraleda.com'
240-
parser = urllib.robotparser.RobotFileParser()
241-
parser.set_url(url)
242-
try:
243-
parser.read()
244-
except URLError:
245-
self.skipTest('%s is unavailable' % url)
246-
self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False)
238+
with support.transient_internet('mueblesmoraleda.com'):
239+
url = 'http://mueblesmoraleda.com'
240+
parser = urllib.robotparser.RobotFileParser()
241+
parser.set_url(url)
242+
try:
243+
parser.read()
244+
except URLError:
245+
self.skipTest('%s is unavailable' % url)
246+
self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False)
247247

248248
def testPythonOrg(self):
249249
support.requires('network')
250-
parser = urllib.robotparser.RobotFileParser(
251-
"http://www.python.org/robots.txt")
252-
parser.read()
253-
self.assertTrue(parser.can_fetch("*",
254-
"http://www.python.org/robots.txt"))
250+
with support.transient_internet('www.python.org'):
251+
parser = urllib.robotparser.RobotFileParser(
252+
"http://www.python.org/robots.txt")
253+
parser.read()
254+
self.assertTrue(
255+
parser.can_fetch("*", "http://www.python.org/robots.txt"))
255256

256257
def test_main():
257258
support.run_unittest(NetworkTestCase)

0 commit comments

Comments
 (0)