|
35 | 35 | "check_warnings", "CleanImport", "EnvironmentVarGuard", |
36 | 36 | "TransientResource", "captured_output", "captured_stdout", |
37 | 37 | "time_out", "socket_peer_reset", "ioerror_peer_reset", |
38 | | - "run_with_locale", 'temp_umask', |
| 38 | + "run_with_locale", 'temp_umask', "transient_internet", |
39 | 39 | "set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner", |
40 | 40 | "run_unittest", "run_doctest", "threading_setup", "threading_cleanup", |
41 | 41 | "reap_children", "cpython_only", "check_impl_detail", "get_attribute", |
@@ -775,23 +775,47 @@ def __exit__(self, type_=None, value=None, traceback=None): |
775 | 775 | else: |
776 | 776 | raise ResourceDenied("an optional resource is not available") |
777 | 777 |
|
778 | | - |
779 | 778 | # Context managers that raise ResourceDenied when various issues |
780 | 779 | # with the Internet connection manifest themselves as exceptions. |
| 780 | +# XXX deprecate these and use transient_internet() instead |
781 | 781 | time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) |
782 | 782 | socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) |
783 | 783 | ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) |
784 | 784 |
|
785 | 785 |
|
786 | 786 | @contextlib.contextmanager |
787 | | -def transient_internet(): |
| 787 | +def transient_internet(resource_name, *, timeout=30.0, errnos=()): |
788 | 788 | """Return a context manager that raises ResourceDenied when various issues |
789 | 789 | with the Internet connection manifest themselves as exceptions.""" |
790 | | - time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) |
791 | | - socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) |
792 | | - ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) |
793 | | - with time_out, socket_peer_reset, ioerror_peer_reset: |
| 790 | + denied = ResourceDenied("Resource '%s' is not available" % resource_name) |
| 791 | + captured_errnos = errnos or (errno.ETIMEDOUT, errno.ECONNRESET) |
| 792 | + |
| 793 | + def filter_error(err): |
| 794 | + if (isinstance(err, socket.timeout) or |
| 795 | + getattr(err, 'errno', None) in captured_errnos): |
| 796 | + if not verbose: |
| 797 | + sys.stderr.write(denied.args[0] + "\n") |
| 798 | + raise denied from err |
| 799 | + |
| 800 | + old_timeout = socket.getdefaulttimeout() |
| 801 | + try: |
| 802 | + if timeout is not None: |
| 803 | + socket.setdefaulttimeout(timeout) |
794 | 804 | yield |
| 805 | + except IOError as err: |
| 806 | + # socket.error inherits IOError |
| 807 | + 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]) |
| 814 | + raise |
| 815 | + # XXX should we catch generic exceptions and look for their |
| 816 | + # __cause__ or __context__? |
| 817 | + finally: |
| 818 | + socket.setdefaulttimeout(old_timeout) |
795 | 819 |
|
796 | 820 |
|
797 | 821 | @contextlib.contextmanager |
|
0 commit comments