Repository: jborean93/smbprotocol

Issue:
smbprotocol.connection.Connection.connect() no longer shows the low-level connection error
Since 1.11.0  smbprotocol.connection.Connection.connect() no longer shows the low-level connection error

This is a change introduced in 1.11.0 

The change is in transport.py

```diff
-raise ValueError("Failed to connect to '%s:%s': %s" % (self.server, self.port, str(err))) from err
+raise ValueError(f"Failed to connect to '{self.server}:{self.port}'") from err
```

https://github.com/jborean93/smbprotocol/compare/v1.10.1...v1.11.0#diff-1037611d131586bd4201108e4c80be46a17a6a1a365766e7190be44673eac0ffL70


It helps to know more about why a connection failed... it can be a DNS error for example.

To reproduce, use this code

```python
from smbprotocol.connection import Connection
conn = Connection('some-id', 'localhost', 1234)
conn.connect()
```

On 1.11.0 the error is just `ValueError: Failed to connect to 'localhost:1234'`
On 1.10.1 the error is `ValueError: Failed to connect to 'localhost:1234': [Errno 111] Connection refused`


Below are the stack traces 

-------------------

1.11.0

```
Python 3.11.7 (4666189, Dec 11 2023, 13:04:52) [GCC 7.3.1 20180712 (Red Hat 7.3.1-17)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from smbprotocol.connection import Connection
conn = Connection('some-id', 'localhost', 1234)
conn.connect()
>>> >>> Traceback (most recent call last):
  File "/home/adi/chevah/server/build-py3/lib/python3.11/site-packages/smbprotocol/transport.py", line 66, in connect
    self._sock = socket.create_connection((self.server, self.port), timeout=self.timeout)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/adi/chevah/server/build-py3/lib/python3.11/socket.py", line 851, in create_connection
    raise exceptions[0]
  File "/home/adi/chevah/server/build-py3/lib/python3.11/socket.py", line 836, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/adi/chevah/server/build-py3/lib/python3.11/site-packages/smbprotocol/connection.py", line 863, in connect
    self.transport.connect()
  File "/home/adi/chevah/server/build-py3/lib/python3.11/site-packages/smbprotocol/transport.py", line 68, in connect
    raise ValueError(f"Failed to connect to '{self.server}:{self.port}'") from err
ValueError: Failed to connect to 'localhost:1234'

```





------------

1.10.1

```
Python 3.11.7 (4666189, Dec 11 2023, 13:04:52) [GCC 7.3.1 20180712 (Red Hat 7.3.1-17)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from smbprotocol.connection import Connection
conn = Connection('some-id', 'localhost', 1234)
conn.connect()
>>> >>> Traceback (most recent call last):
  File "/home/adi/chevah/server/build-py3/lib/python3.11/site-packages/smbprotocol/transport.py", line 68, in connect
    self._sock = socket.create_connection((self.server, self.port), timeout=self.timeout)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/adi/chevah/server/build-py3/lib/python3.11/socket.py", line 851, in create_connection
    raise exceptions[0]
  File "/home/adi/chevah/server/build-py3/lib/python3.11/socket.py", line 836, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/adi/chevah/server/build-py3/lib/python3.11/site-packages/smbprotocol/connection.py", line 861, in connect
    self.transport.connect()
  File "/home/adi/chevah/server/build-py3/lib/python3.11/site-packages/smbprotocol/transport.py", line 70, in connect
    raise ValueError("Failed to connect to '%s:%s': %s" % (self.server, self.port, str(err))) from err
ValueError: Failed to connect to 'localhost:1234': [Errno 111] Connection refused
```


Comments:

Patch:
diff --git a/src/smbprotocol/transport.py b/src/smbprotocol/transport.py
index 052b43a..2288239 100644
--- a/src/smbprotocol/transport.py
+++ b/src/smbprotocol/transport.py
@@ -65,7 +65,7 @@ class Tcp:
                 try:
                     self._sock = socket.create_connection((self.server, self.port), timeout=self.timeout)
                 except (OSError, socket.gaierror) as err:
-                    raise ValueError(f"Failed to connect to '{self.server}:{self.port}'") from err
+                    raise ValueError(f"Failed to connect to '{self.server}:{self.port}': {err}") from err
                 self._sock.settimeout(None)  # Make sure the socket is in blocking mode.
                 self.connected = True
 


Test patch:
diff --git a/tests/test_transport.py b/tests/test_transport.py
index 960350c..0108dea 100644
--- a/tests/test_transport.py
+++ b/tests/test_transport.py
@@ -72,8 +72,15 @@ class TestTcp:
         )
 
     def test_invalid_host(self):
+        """
+        Raises ValueError when failing to connect to the remote server.
+
+        The error message contains the low-level OS error details.
+        """
         tcp = Tcp("fake-host", 445)
-        with pytest.raises(ValueError, match=re.escape("Failed to connect to 'fake-host:445'")):
+        # We just check for OSError marker, as the actual error message
+        # might be different based on current OS.
+        with pytest.raises(ValueError, match=r"Failed to connect to 'fake-host:445': \[Errno .*"):
             tcp.connect()