diff --git a/docs/releases.rst b/docs/releases.rst index 126a9c0..80e6188 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -2,6 +2,11 @@ Release History =============== +1.2.14 +====== + +- |:sparkles:| add support for hostnames in connection path + 1.2.13 ====== diff --git a/pycomm3/_version.py b/pycomm3/_version.py index db40769..40b911c 100644 --- a/pycomm3/_version.py +++ b/pycomm3/_version.py @@ -22,5 +22,5 @@ # SOFTWARE. # -__version_info__ = (1, 2, 13) +__version_info__ = (1, 2, 14) __version__ = ".".join(f"{x}" for x in __version_info__) diff --git a/pycomm3/cip_driver.py b/pycomm3/cip_driver.py index 0eab6e6..4c3f72f 100644 --- a/pycomm3/cip_driver.py +++ b/pycomm3/cip_driver.py @@ -305,7 +305,7 @@ def open(self): return True try: if self._sock is None: - self._sock = Socket(self._cfg["socket_timout"]) + self._sock = Socket(self._cfg["socket_timeout"]) self.__log.debug(f'Opening connection to {self._cfg["ip address"]}') self._sock.connect(self._cfg["ip address"], self._cfg["port"]) self._connection_opened = True @@ -614,17 +614,12 @@ def parse_connection_path(path: str, auto_slot: bool = False) -> Tuple[str, Opti except Exception as err: raise RequestError(f'Invalid port: {port}') else: - if 0 > port >= 65535: + if port <= 0 or port >= 65535: raise RequestError(f'Invalid port: {port}') else: port = None - try: - ipaddress.ip_address(ip) - except ValueError as err: - raise RequestError(f"Invalid IP Address: {ip}") from err - _path = parse_cip_route(route, auto_slot) except RequestError: diff --git a/pycomm3/socket_.py b/pycomm3/socket_.py index 5869d8d..0348f20 100644 --- a/pycomm3/socket_.py +++ b/pycomm3/socket_.py @@ -40,9 +40,9 @@ def __init__(self, timeout=5.0): def connect(self, host, port): try: - self.sock.connect((host, port)) - except socket.timeout: - raise CommError("Socket timeout during connection.") + self.sock.connect((socket.gethostbyname(host), port)) + except socket.error: + raise CommError(f"Failed to open socket to {host}:{port}") def send(self, msg, timeout=0): if timeout != 0: diff --git a/tests/offline/test_cip_driver.py b/tests/offline/test_cip_driver.py index 3d018c2..8f8e81d 100644 --- a/tests/offline/test_cip_driver.py +++ b/tests/offline/test_cip_driver.py @@ -82,14 +82,6 @@ def test_plc_path_auto_slot(path, expected_output): _bad_paths = [ - "192", - "192.168", - "192.168.1", - "192.168.1.1.100", - "300.1.1.1", - "1.300.1.1", - "1.1.300.1", - "1.1.1.300", "192.168.1.100/Z", "bp/0", "192.168.1.100/-1", @@ -101,7 +93,7 @@ def test_plc_path_auto_slot(path, expected_output): "192.168.1.100,", "192.168.1.100:abc", "192.168.1.100:/bp/0", - "192.168.100:-123", + "192.168.1.100:-123", ] diff --git a/tests/offline/test_socket_.py b/tests/offline/test_socket_.py index aaf6973..0041a0f 100644 --- a/tests/offline/test_socket_.py +++ b/tests/offline/test_socket_.py @@ -31,6 +31,24 @@ def test_socket_init_creates_socket(): assert my_sock mock_socket.assert_called_once() + +def test_socket_connect_raises_commerror_on_failed_host_lookup(): + """Test the Socket.connect method. + + This test covers both the calling of Python socket's connect and + the pycomm exception being raised. + """ + with mock.patch.object(socket.socket, 'connect') as mock_socket_connect: + with mock.patch.object(socket, 'gethostbyname') as mock_socket_gethost: + mock_socket_gethost.side_effect = socket.gaierror + my_sock = Socket() + with pytest.raises(CommError): + my_sock.connect('123.456.789.101', 12345) + + mock_socket_connect.assert_not_called() + mock_socket_gethost.assert_called_once() + + def test_socket_connect_raises_commerror_on_timeout(): """Test the Socket.connect method. @@ -38,12 +56,15 @@ def test_socket_connect_raises_commerror_on_timeout(): the pycomm exception being raised. """ with mock.patch.object(socket.socket, 'connect') as mock_socket_connect: - mock_socket_connect.side_effect = socket.timeout - my_sock = Socket() - with pytest.raises(CommError): - my_sock.connect('123.456.789.101', 12345) + with mock.patch.object(socket, 'gethostbyname') as mock_socket_gethost: + mock_socket_connect.side_effect = socket.timeout + my_sock = Socket() + with pytest.raises(CommError): + my_sock.connect('123.456.789.101', 12345) mock_socket_connect.assert_called_once() + mock_socket_gethost.assert_called_once() + def test_socket_send_raises_commerror_on_no_bytes_sent(): TEST_MSG = b"Meaningless Data" diff --git a/tests/offline/test_types.py b/tests/offline/test_types.py index 33faeed..d9a776b 100644 --- a/tests/offline/test_types.py +++ b/tests/offline/test_types.py @@ -4,7 +4,7 @@ PLC_INFOS = [ - {'vendor': 'Honeywell Inc.', + {'vendor': 'Rockwell Automation/Allen-Bradley', 'product_type': 'Limit Switch', 'product_code': 0x03, 'revision': {'major': 12, 'minor': 34},