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

Code before:
        current_usage = await asyncio.shield(self._decrement_usage())
        if current_usage == 0:
            # This was the last active context, so disconnect the pool.
            await asyncio.shield(self.aclose())

    _DEL_MESSAGE = "Unclosed Redis client"

    # passing _warnings and _grl as argument default since they may be gone
    # by the time __del__ is called at shutdown
    def __del__(
        self,
        _warn: Any = warnings.warn,
        _grl: Any = asyncio.get_running_loop,
    ) -> None:
        if hasattr(self, "connection") and (self.connection is not None):
            _warn(f"Unclosed client session {self!r}", ResourceWarning, source=self)
            try:
                context = {"client": self, "message": self._DEL_MESSAGE}
                _grl().call_exception_handler(context)
            except RuntimeError:
                pass
            self.connection._close()

    async def aclose(self, close_connection_pool: Optional[bool] = None) -> None:
        """
        Closes Redis client connection

        Args:
            close_connection_pool:
                decides whether to close the connection pool used by this Redis client,
                overriding Redis.auto_close_connection_pool.
                By default, let Redis.auto_close_connection_pool decide
                whether to close the connection pool.
        """
        conn = self.connection
        if conn:
            self.connection = None
            await self.connection_pool.release(conn)
        if close_connection_pool or (
            close_connection_pool is None and self.auto_close_connection_pool
        ):
            await self.connection_pool.disconnect()

    @deprecated_function(version="5.0.1", reason="Use aclose() instead", name="close")
    async def close(self, close_connection_pool: Optional[bool] = None) -> None:
        """
        Alias for aclose(), for backwards compatibility
        """
        await self.aclose(close_connection_pool)

    async def _send_command_parse_response(self, conn, command_name, *args, **options):

Review comment (refactor):
A helper method is required so we can use it in the `shield()`.

Code after:

    async def __aenter__(self: _RedisT) -> _RedisT:
        """
        Async context manager entry. Increments a usage counter so that the
        connection pool is only closed (via aclose()) when no context is using
        the client.
        """
        await self._increment_usage()
        try:
            # Initialize the client (i.e. establish connection, etc.)
            return await self.initialize()
        except Exception:
            # If initialization fails, decrement the counter to keep it in sync
            await self._decrement_usage()
            raise

    async def _increment_usage(self) -> int:
        """
        Helper coroutine to increment the usage counter while holding the lock.
        Returns the new value of the usage counter.
        """
        async with self._usage_lock:
            self._usage_counter += 1
            return self._usage_counter

    async def _decrement_usage(self) -> int:
        """
        Helper coroutine to decrement the usage counter while holding the lock.
        Returns the new value of the usage counter.
        """
        async with self._usage_lock:
            self._usage_counter -= 1
            return self._usage_counter

    async def __aexit__(self, exc_type, exc_value, traceback):
        """
        Async context manager exit. Decrements a usage counter. If this is the
        last exit (counter becomes zero), the client closes its connection pool.
        """
        current_usage = await asyncio.shield(self._decrement_usage())
        if current_usage == 0:
            # This was the last active context, so disconnect the pool.
            await asyncio.shield(self.aclose())

    _DEL_MESSAGE = "Unclosed Redis client"

    # passing _warnings and _grl as argument default since they may be gone
    # by the time __del__ is called at shutdown
    def __del__(
        self,
        _warn: Any = warnings.warn,