Repository: redis/redis-py
File: redis/asyncio/connection.py (Python)

Code before:

    async def can_read(self, timeout: float = 0):
        """Poll the socket to see if there's data that can be read."""
        if not self.is_connected:
            await self.connect()
        try:
            return await self._parser.can_read(timeout)
        except OSError as e:
            await self.disconnect()
            raise ConnectionError(
                f"Error while reading from {self.host}:{self.port}: {e.args}"
            )

    async def read_response(
        self,
        disable_decoding: bool = False,
        timeout: Optional[float] = None,
        with_lock: bool = True,
    ):
        """Read the response from a previously sent command"""
        read_timeout = timeout if timeout is not None else self.socket_timeout
        lock_ctxt = self._lock if with_lock else nullcontext
        if not self.is_connected:
            await self.connect()
        try:
            async with lock_ctxt, async_timeout.timeout(read_timeout):
                response = await self._parser.read_response(
                    disable_decoding=disable_decoding
                )
        except asyncio.TimeoutError:
            if timeout is not None:
                # user requested timeout, return None
                return None
            # it was a self.socket_timeout error.
            await self.disconnect()
            raise TimeoutError(f"Timeout reading from {self.host}:{self.port}")
        except OSError as e:
            await self.disconnect()
            raise ConnectionError(
                f"Error while reading from {self.host}:{self.port} : {e.args}"
            )
        except BaseException:
            await self.disconnect()
            raise

        if self.health_check_interval:
            if sys.version_info[0:2] == (3, 6):
                func = asyncio.get_event_loop
            else:
                func = asyncio.get_running_loop
            self.next_health_check = func().time() + self.health_check_interval

Review comment (performance):
Wouldn't an if/else on with_lock directly be more performant instead of a null context? Same for async_timeout. Even with 0 timeout, async_timeout adds significant latency.

Code after:

    async def read_response(
        self,
        disable_decoding: bool = False,
        timeout: Optional[float] = None,
    ):
        """Read the response from a previously sent command"""
        read_timeout = timeout if timeout is not None else self.socket_timeout
        try:
            if read_timeout is not None:
                async with async_timeout.timeout(read_timeout):
                    response = await self._parser.read_response(
                        disable_decoding=disable_decoding
                    )
            else:
                response = await self._parser.read_response(
                    disable_decoding=disable_decoding
                )
        except asyncio.TimeoutError:
            if timeout is not None:
                # user requested timeout, return None
                return None
            # it was a self.socket_timeout error.
            await self.disconnect(nowait=True)
            raise TimeoutError(f"Timeout reading from {self.host}:{self.port}")
        except OSError as e:
            await self.disconnect(nowait=True)
            raise ConnectionError(
                f"Error while reading from {self.host}:{self.port} : {e.args}"
            )
        except asyncio.CancelledError:
            # need this check for 3.7, where CancelledError
            # is subclass of Exception, not BaseException
            raise
        except Exception:
            await self.disconnect(nowait=True)
            raise

        if self.health_check_interval:
            next_time = asyncio.get_running_loop().time() + self.health_check_interval
            self.next_health_check = next_time

        if isinstance(response, ResponseError):
            raise response from None
        return response

    def pack_command(self, *args: EncodableT) -> List[bytes]:
        """Pack a series of arguments into the Redis protocol"""
        output = []
        # the client might have included 1 or more literal arguments in
        # the command name, e.g., 'CONFIG GET'. The Redis server expects these