Repository: redis/redis-py

Issue:
'UnixDomainSocketConnection' object has no attribute '_command_packer'
**Version**: v4.5.0

**Platform**: Debian Bullseye using Python v3.9.2

**Description**: 
The following code which used to work on up to and including v4.4.2 now crashes with the stack trace below on v4.5.0.

```python
redis_conn = redis.Redis(
        unix_socket_path="/var/run/redis/redis-server.sock",
        decode_responses=True)
redis_conn.ping()
```

```
Traceback (most recent call last):
  File "<redacted>", line 142, in main
    redis_conn.ping()
  File "/usr/local/lib/python3.9/dist-packages/redis/commands/core.py", line 1194, in ping
    return self.execute_command("PING", **kwargs)
  File "/usr/local/lib/python3.9/dist-packages/redis/client.py", line 1258, in execute_command
    return conn.retry.call_with_retry(
  File "/usr/local/lib/python3.9/dist-packages/redis/retry.py", line 46, in call_with_retry
    return do()
  File "/usr/local/lib/python3.9/dist-packages/redis/client.py", line 1259, in <lambda>
    lambda: self._send_command_parse_response(
  File "/usr/local/lib/python3.9/dist-packages/redis/client.py", line 1234, in _send_command_parse_response
    conn.send_command(*args)
  File "/usr/local/lib/python3.9/dist-packages/redis/connection.py", line 916, in send_command
    self._command_packer.pack(*args),
AttributeError: 'UnixDomainSocketConnection' object has no attribute '_command_packer'
```

Discussion:
codecov-commenter: # [Codecov](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis) Report
Base: **92.28**% // Head: **77.42**% // Decreases project coverage by **`-14.87%`** :warning:
> Coverage data is based on head [(`b6664d3`)](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis) compared to base [(`5cb5712`)](https://codecov.io/gh/redis/redis-py/commit/5cb5712d283fa8fb300abc9d71a61c1a81de5643?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis).
> Patch coverage: 100.00% of modified lines in pull request are covered.

:mega: This organization is not using Codecov’s [GitHub App Integration](https://github.com/apps/codecov). We recommend you install it so Codecov can continue to function properly for your repositories. [Learn more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis)

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           master    #2583       +/-   ##
===========================================
- Coverage   92.28%   77.42%   -14.87%     
===========================================
  Files         115      115               
  Lines       29736    28843      -893     
===========================================
- Hits        27442    22331     -5111     
- Misses       2294     6512     +4218     
```


| [Impacted Files](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis) | Coverage Δ | |
|---|---|---|
| [redis/connection.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-cmVkaXMvY29ubmVjdGlvbi5weQ==) | `78.22% <100.00%> (-8.56%)` | :arrow_down: |
| [tests/test\_connection.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvdGVzdF9jb25uZWN0aW9uLnB5) | `97.45% <100.00%> (-0.79%)` | :arrow_down: |
| [tests/test\_cluster.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvdGVzdF9jbHVzdGVyLnB5) | `14.29% <0.00%> (-82.58%)` | :arrow_down: |
| [tests/test\_asyncio/test\_cluster.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvdGVzdF9hc3luY2lvL3Rlc3RfY2x1c3Rlci5weQ==) | `18.41% <0.00%> (-79.08%)` | :arrow_down: |
| [redis/asyncio/cluster.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-cmVkaXMvYXN5bmNpby9jbHVzdGVyLnB5) | `16.96% <0.00%> (-74.72%)` | :arrow_down: |
| [redis/cluster.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-cmVkaXMvY2x1c3Rlci5weQ==) | `18.68% <0.00%> (-71.75%)` | :arrow_down: |
| [redis/asyncio/parser.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-cmVkaXMvYXN5bmNpby9wYXJzZXIucHk=) | `17.30% <0.00%> (-63.47%)` | :arrow_down: |
| [redis/commands/cluster.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-cmVkaXMvY29tbWFuZHMvY2x1c3Rlci5weQ==) | `36.95% <0.00%> (-57.07%)` | :arrow_down: |
| [redis/crc.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-cmVkaXMvY3JjLnB5) | `45.45% <0.00%> (-54.55%)` | :arrow_down: |
| [tests/mocks.py](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis#diff-dGVzdHMvbW9ja3MucHk=) | `68.96% <0.00%> (-27.59%)` | :arrow_down: |
| ... and [66 more](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/redis/redis-py/pull/2583?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=redis).


Patch:
diff --git a/redis/connection.py b/redis/connection.py
index 2461482..d35980c 100644
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -1153,6 +1153,7 @@ class UnixDomainSocketConnection(Connection):
         retry=None,
         redis_connect_func=None,
         credential_provider: Optional[CredentialProvider] = None,
+        command_packer=None,
     ):
         """
         Initialize a new UnixDomainSocketConnection.
@@ -1202,6 +1203,7 @@ class UnixDomainSocketConnection(Connection):
         self.set_parser(parser_class)
         self._connect_callbacks = []
         self._buffer_cutoff = 6000
+        self._command_packer = self._construct_command_packer(command_packer)
 
     def repr_pieces(self):
         pieces = [("path", self.path), ("db", self.db)]


Test patch:
diff --git a/tests/test_connection.py b/tests/test_connection.py
index e0b53cd..25b4118 100644
--- a/tests/test_connection.py
+++ b/tests/test_connection.py
@@ -7,7 +7,13 @@ import pytest
 
 import redis
 from redis.backoff import NoBackoff
-from redis.connection import Connection, HiredisParser, PythonParser
+from redis.connection import (
+    Connection,
+    HiredisParser,
+    PythonParser,
+    SSLConnection,
+    UnixDomainSocketConnection,
+)
 from redis.exceptions import ConnectionError, InvalidResponse, TimeoutError
 from redis.retry import Retry
 from redis.utils import HIREDIS_AVAILABLE
@@ -163,3 +169,39 @@ def test_connection_parse_response_resume(r: redis.Redis, parser_class):
         pytest.fail("didn't receive a response")
     assert response
     assert i > 0
+
+
+@pytest.mark.onlynoncluster
+@pytest.mark.parametrize(
+    "Class",
+    [
+        Connection,
+        SSLConnection,
+        UnixDomainSocketConnection,
+    ],
+)
+def test_pack_command(Class):
+    """
+    This test verifies that the pack_command works
+    on all supported connections. #2581
+    """
+    cmd = (
+        "HSET",
+        "foo",
+        "key",
+        "value1",
+        b"key_b",
+        b"bytes str",
+        b"key_i",
+        67,
+        "key_f",
+        3.14159265359,
+    )
+    expected = (
+        b"*10\r\n$4\r\nHSET\r\n$3\r\nfoo\r\n$3\r\nkey\r\n$6\r\nvalue1\r\n"
+        b"$5\r\nkey_b\r\n$9\r\nbytes str\r\n$5\r\nkey_i\r\n$2\r\n67\r\n$5"
+        b"\r\nkey_f\r\n$13\r\n3.14159265359\r\n"
+    )
+
+    actual = Class().pack_command(*cmd)[0]
+    assert actual == expected, f"actual = {actual}, expected = {expected}"