Repository: bitcoin/bitcoin
File: test/functional/test_framework/test_node.py (Python)

Code before:
                    # overhead is trivial, and the added guarantees are worth
                    # the minimal performance cost.
                self.log.debug("RPC successfully started")
                if self.use_cli:
                    return
                self.rpc = rpc
                self.rpc_connected = True
                self.url = self.rpc.rpc_url
                return
            except JSONRPCException as e:  # Initialization phase
                # -28 RPC in warmup
                # -342 Service unavailable, RPC server started but is shutting down due to error
                if e.error['code'] != -28 and e.error['code'] != -342:
                    raise  # unknown JSON RPC exception
            except ConnectionResetError:
                # This might happen when the RPC server is in warmup, but shut down before the call to getblockcount
                # succeeds. Try again to properly raise the FailedToStartError
                pass
            except OSError as e:
                if e.errno == errno.ETIMEDOUT:
                    pass  # Treat identical to ConnectionResetError
                elif e.errno == errno.ECONNREFUSED:
                    pass  # Port not yet open?
                else:
                    raise  # unknown OS error
            except ValueError as e:  # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting
                if "No RPC credentials" not in str(e):
                    raise
            time.sleep(1.0 / poll_per_s)
        self._raise_assertion_error("Unable to connect to bitcoind after {}s".format(self.rpc_timeout))

    def wait_for_cookie_credentials(self):
        """Ensures auth cookie credentials can be read, e.g. for testing CLI with -rpcwait before RPC connection is up."""
        self.log.debug("Waiting for cookie credentials")
        # Poll at a rate of four times per second.
        poll_per_s = 4
        for _ in range(poll_per_s * self.rpc_timeout):
            try:
                get_auth_cookie(self.datadir_path, self.chain)
                self.log.debug("Cookie credentials successfully retrieved")
                return
            except ValueError:  # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting
                pass            # so we continue polling until RPC credentials are retrieved
            time.sleep(1.0 / poll_per_s)
        self._raise_assertion_error("Unable to retrieve cookie credentials after {}s".format(self.rpc_timeout))

    def generate(self, nblocks, maxtries=1000000, **kwargs):
        self.log.debug("TestNode.generate() dispatches `generate` call to `generatetoaddress`")
        return self.generatetoaddress(nblocks=nblocks, address=self.get_deterministic_priv_key().address, maxtries=maxtries, **kwargs)

    def generateblock(self, *args, called_by_framework, **kwargs):

Review comment (suggestion):
The inclusion of an assert suggests to me that we expect that all OSErrors without errno are TimeoutErrors? Is this actually what we expect?

If not should the if statement be changed to `if error_num is None and isinstance(e, TimeoutError):`

Code after:
                    # and prevent racy tests and intermittent failures as much
                    # as possible. Some tests might not need this, but the
                    # overhead is trivial, and the added guarantees are worth
                    # the minimal performance cost.
                self.log.debug("RPC successfully started")
                # Set rpc_connected even if we are in use_cli mode so that we know we can call self.stop() if needed.
                self.rpc_connected = True
                if self.use_cli:
                    return
                self.rpc = rpc
                self.url = self.rpc.rpc_url
                return
            except JSONRPCException as e:
                # Suppress these as they are expected during initialization.
                # -28 RPC in warmup
                # -342 Service unavailable, could be starting up or shutting down
                if e.error['code'] not in [-28, -342]:
                    raise  # unknown JSON RPC exception
                suppressed_errors[f"JSONRPCException {e.error['code']}"] += 1
                latest_error = repr(e)
            except OSError as e:
                error_num = e.errno
                # Work around issue where socket timeouts don't have errno set.
                # https://github.com/python/cpython/issues/109601
                if error_num is None and isinstance(e, TimeoutError):
                    error_num = errno.ETIMEDOUT

                # Suppress similarly to the above JSONRPCException errors.
                if error_num not in [
                    errno.ECONNRESET,   # This might happen when the RPC server is in warmup,
                                        # but shut down before the call to getblockcount succeeds.
                    errno.ETIMEDOUT,    # Treat identical to ECONNRESET
                    errno.ECONNREFUSED  # Port not yet open?
                ]:
                    raise  # unknown OS error
                suppressed_errors[f"OSError {errno.errorcode[error_num]}"] += 1
                latest_error = repr(e)
            except ValueError as e:
                # Suppress if cookie file isn't generated yet and no rpcuser or rpcpassword; bitcoind may be starting.
                if "No RPC credentials" not in str(e):
                    raise
                suppressed_errors["missing_credentials"] += 1
                latest_error = repr(e)
            time.sleep(1.0 / poll_per_s)
        self._raise_assertion_error(f"Unable to connect to bitcoind after {self.rpc_timeout}s (ignored errors: {str(dict(suppressed_errors))}, latest error: {latest_error})")

    def wait_for_cookie_credentials(self):
        """Ensures auth cookie credentials can be read, e.g. for testing CLI with -rpcwait before RPC connection is up."""
        self.log.debug("Waiting for cookie credentials")
        # Poll at a rate of four times per second.
        poll_per_s = 4