# Path: puresnmp/exc.py
# class Timeout(socket.timeout):
#     """
#     Wrapper for network timeouts.
# 
#     This wraps both "socket.timeout" and "asyncio.TimeoutError"
#     """
# 
#     def __init__(self, message: str) -> None:
#         super().__init__()
#         self.message = message
# 
# Path: puresnmp/typevars.py
# class SocketInfo:
#     """
#     A simple tuple containing an IP address and port number
#     """
# 
#     address: str
#     port: int
# 
# class SocketResponse:
#     """
#     Metadata for socket responses
#     """
# 
#     data: bytes
#     info: SocketInfo


import logging
import socket
from ipaddress import ip_address
from time import time
from typing import TYPE_CHECKING, Generator
from x690.util import visible_octets  # type: ignore
from .exc import Timeout
from .typevars import SocketInfo, SocketResponse
    from typing import Optional
        if checked_ip.version == 4:
            address_family = socket.AF_INET
        else:
            address_family = socket.AF_INET6

        sock = socket.socket(address_family, socket.SOCK_DGRAM)
        sock.settimeout(timeout or self.timeout)

        for num_retry in range(self.retries):
            try:
                if LOG.isEnabledFor(logging.DEBUG):
                    hexdump = visible_octets(packet)
                    LOG.debug(
                        "Sending packet to %s:%s (attempt %d/%d)\n%s",
                        ip,
                        port,
                        (num_retry + 1),
                        self.retries,
                        hexdump,
                    )
                sock.sendto(packet, (ip, port))
                response = sock.recv(self.buffer_size)
                break
            except socket.timeout:
                LOG.debug(
                    "Timeout during attempt #%d", (num_retry + 1)
                )  # TODO add detail
                continue
        else:
            sock.close()

# Path: puresnmp/exc.py
# class Timeout(socket.timeout):
#     """
#     Wrapper for network timeouts.
# 
#     This wraps both "socket.timeout" and "asyncio.TimeoutError"
#     """
# 
#     def __init__(self, message: str) -> None:
#         super().__init__()
#         self.message = message
# 
# Path: puresnmp/typevars.py
# class SocketInfo:
#     """
#     A simple tuple containing an IP address and port number
#     """
# 
#     address: str
#     port: int
# 
# class SocketResponse:
#     """
#     Metadata for socket responses
#     """
# 
#     data: bytes
#     info: SocketInfo


# Path: puresnmp/transport.py
import logging
import socket
from ipaddress import ip_address
from time import time
from typing import TYPE_CHECKING, Generator
from x690.util import visible_octets  # type: ignore
from .exc import Timeout
from .typevars import SocketInfo, SocketResponse
    from typing import Optional

        if checked_ip.version == 4:
            address_family = socket.AF_INET
        else:
            address_family = socket.AF_INET6

        sock = socket.socket(address_family, socket.SOCK_DGRAM)
        sock.settimeout(timeout or self.timeout)

        for num_retry in range(self.retries):
            try:
                if LOG.isEnabledFor(logging.DEBUG):
                    hexdump = visible_octets(packet)
                    LOG.debug(
                        "Sending packet to %s:%s (attempt %d/%d)\n%s",
                        ip,
                        port,
                        (num_retry + 1),
                        self.retries,
                        hexdump,
                    )
                sock.sendto(packet, (ip, port))
                response = sock.recv(self.buffer_size)
                break
            except socket.timeout:
                LOG.debug(
                    "Timeout during attempt #%d", (num_retry + 1)
                )  # TODO add detail
                continue
        else:
            sock.close()

            raise Timeout("Max of %d retries reached" % self.retries)