Skip to content

Doctor

Collect comprehensive diagnostic information for troubleshooting and remote support. The DiagnosticInfo class gathers system, environment, and device information in a structured format compatible with all output formats.


Architecture

Diagnostic Information Structure

classDiagram
    class DiagnosticInfo {
        +library: LibraryInfo
        +python: PythonInfo
        +platform: PlatformInfo
        +device: DeviceInfo
        +collect(device) DiagnosticInfo
    }

    class LibraryInfo {
        +kazunoko_version: str
        +kazunoko_path: str
        +pydantic_version: str
        +pyserial_version: str
        +typer_version: str
        +loguru_version: str
        +collect() LibraryInfo
    }

    class PythonInfo {
        +python_version: str
        +python_executable: str
        +python_prefix: str
        +venv_type: str | None
        +collect() PythonInfo
    }

    class PlatformInfo {
        +os_system: str
        +os_release: str
        +os_version: str
        +architecture: str
        +python_implementation: str
        +collect() PlatformInfo
    }

    class DeviceInfo {
        +connected_port: str
        +firmware_version: str | None
        +mac_address: str | None
        +device_status: str | None
        +uptime: int | None
        +collect(device) DeviceInfo
    }

    DiagnosticInfo --> LibraryInfo
    DiagnosticInfo --> PythonInfo
    DiagnosticInfo --> PlatformInfo
    DiagnosticInfo --> DeviceInfo

DiagnosticInfo

kazunoko.doctor.DiagnosticInfo

Diagnostic information for troubleshooting.

Collected system and device information organized into categories and formatted with ResponseFormatter for various output formats.

Attributes:

Name Type Description
library LibraryInfo

Library version information (kazunoko and dependencies)

python PythonInfo

Python environment information (version, venv type)

platform PlatformInfo

Platform and OS information (system, architecture)

device DeviceInfo

Device connection and status information (port, firmware, status)

Source code in src/kazunoko/doctor.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
@dataclass
class DiagnosticInfo:
    """
    Diagnostic information for troubleshooting.

    Collected system and device information organized into categories
    and formatted with ResponseFormatter for various output formats.

    Attributes:
        library: Library version information (kazunoko and dependencies)
        python: Python environment information (version, venv type)
        platform: Platform and OS information (system, architecture)
        device: Device connection and status information (port, firmware, status)
    """
    library: LibraryInfo = Field(..., description="Library version information")
    python: PythonInfo = Field(..., description="Python environment information")
    platform: PlatformInfo = Field(..., description="Platform and OS information")
    device: DeviceInfo = Field(..., description="Device connection and status")

    @classmethod
    def collect(cls, device: DeviceProtocol) -> "DiagnosticInfo":
        """
        Collect all diagnostic information.

        Gathers system, environment, and device information for troubleshooting purposes.

        Args:
            device: Connected DeviceProtocol instance

        Returns:
            DiagnosticInfo: Complete diagnostic information

        Example:
            ```python
            from kazunoko import connect
            from kazunoko.doctor import DiagnosticInfo

            # Collect diagnostics from connected device
            with connect() as device:
                diagnostics = DiagnosticInfo.collect(device)
            ```
        """
        logger.debug("Collecting diagnostic information")

        # Collect all information using class methods
        library_info = LibraryInfo.collect()
        python_info = PythonInfo.collect()
        platform_info = PlatformInfo.collect()
        device_info = DeviceInfo.collect(device)

        logger.debug("Diagnostic information collected successfully")

        return cls(
            library=library_info,
            python=python_info,
            platform=platform_info,
            device=device_info,
        )

collect(device) classmethod

Collect all diagnostic information.

Gathers system, environment, and device information for troubleshooting purposes.

Parameters:

Name Type Description Default
device DeviceProtocol

Connected DeviceProtocol instance

required

Returns:

Name Type Description
DiagnosticInfo DiagnosticInfo

Complete diagnostic information

Example
from kazunoko import connect
from kazunoko.doctor import DiagnosticInfo

# Collect diagnostics from connected device
with connect() as device:
    diagnostics = DiagnosticInfo.collect(device)
Source code in src/kazunoko/doctor.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
@classmethod
def collect(cls, device: DeviceProtocol) -> "DiagnosticInfo":
    """
    Collect all diagnostic information.

    Gathers system, environment, and device information for troubleshooting purposes.

    Args:
        device: Connected DeviceProtocol instance

    Returns:
        DiagnosticInfo: Complete diagnostic information

    Example:
        ```python
        from kazunoko import connect
        from kazunoko.doctor import DiagnosticInfo

        # Collect diagnostics from connected device
        with connect() as device:
            diagnostics = DiagnosticInfo.collect(device)
        ```
    """
    logger.debug("Collecting diagnostic information")

    # Collect all information using class methods
    library_info = LibraryInfo.collect()
    python_info = PythonInfo.collect()
    platform_info = PlatformInfo.collect()
    device_info = DeviceInfo.collect(device)

    logger.debug("Diagnostic information collected successfully")

    return cls(
        library=library_info,
        python=python_info,
        platform=platform_info,
        device=device_info,
    )

LibraryInfo

kazunoko.doctor.LibraryInfo

Library version information.

Source code in src/kazunoko/doctor.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@dataclass
class LibraryInfo:
    """Library version information."""
    kazunoko_version: str = Field(..., description="kazunoko library version")
    kazunoko_path: str = Field(..., description="kazunoko installation path")
    pydantic_version: str = Field(..., description="pydantic library version")
    pyserial_version: str = Field(..., description="pyserial library version")
    typer_version: str = Field(..., description="typer library version")
    loguru_version: str = Field(..., description="loguru library version")

    @classmethod
    def collect(cls) -> "LibraryInfo":
        """
        Collect kazunoko library and dependency version information.

        Returns:
            LibraryInfo: Library version information
        """
        from . import __version__

        # Get kazunoko installation path
        try:
            import kazunoko
            kazunoko_path = str(Path(kazunoko.__file__).parent)
        except Exception:
            kazunoko_path = "unknown"

        # Get dependency versions
        def get_version(module_name: str) -> str:
            """Get version of a module, return 'unknown' if not available."""
            try:
                module = __import__(module_name)
                return getattr(module, "__version__", "unknown")
            except ImportError:
                return "not installed"

        return cls(
            kazunoko_version=__version__,
            kazunoko_path=kazunoko_path,
            pydantic_version=get_version("pydantic"),
            pyserial_version=get_version("serial"),
            typer_version=get_version("typer"),
            loguru_version=get_version("loguru"),
        )

collect() classmethod

Collect kazunoko library and dependency version information.

Returns:

Name Type Description
LibraryInfo LibraryInfo

Library version information

Source code in src/kazunoko/doctor.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@classmethod
def collect(cls) -> "LibraryInfo":
    """
    Collect kazunoko library and dependency version information.

    Returns:
        LibraryInfo: Library version information
    """
    from . import __version__

    # Get kazunoko installation path
    try:
        import kazunoko
        kazunoko_path = str(Path(kazunoko.__file__).parent)
    except Exception:
        kazunoko_path = "unknown"

    # Get dependency versions
    def get_version(module_name: str) -> str:
        """Get version of a module, return 'unknown' if not available."""
        try:
            module = __import__(module_name)
            return getattr(module, "__version__", "unknown")
        except ImportError:
            return "not installed"

    return cls(
        kazunoko_version=__version__,
        kazunoko_path=kazunoko_path,
        pydantic_version=get_version("pydantic"),
        pyserial_version=get_version("serial"),
        typer_version=get_version("typer"),
        loguru_version=get_version("loguru"),
    )

PythonInfo

kazunoko.doctor.PythonInfo

Python environment information.

Source code in src/kazunoko/doctor.py
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
@dataclass
class PythonInfo:
    """Python environment information."""
    python_version: str = Field(..., description="Python version")
    python_executable: str = Field(..., description="Python executable path")
    python_prefix: str = Field(..., description="sys.prefix (venv path)")
    venv_type: str | None = Field(default=None, description="Virtual environment type")

    @classmethod
    def collect(cls) -> "PythonInfo":
        """
        Collect Python environment information.

        Returns:
            PythonInfo: Python environment information
        """
        # Detect virtual environment type
        venv_type = None
        if hasattr(sys, "real_prefix"):
            venv_type = "virtualenv"
        elif sys.prefix != sys.base_prefix:
            # Check for uv by looking for .venv/pyvenv.cfg with uv marker
            pyvenv_cfg = Path(sys.prefix) / "pyvenv.cfg"
            if pyvenv_cfg.exists():
                try:
                    content = pyvenv_cfg.read_text()
                    if "uv" in content.lower():
                        venv_type = "uv"
                    else:
                        venv_type = "venv"
                except Exception:
                    venv_type = "venv"
            else:
                venv_type = "venv"
        elif "CONDA_DEFAULT_ENV" in sys.prefix or "conda" in sys.prefix.lower():
            venv_type = "conda"

        return cls(
            python_version=sys.version.split()[0],  # e.g., "3.11.5"
            python_executable=sys.executable,
            python_prefix=sys.prefix,
            venv_type=venv_type,
        )

collect() classmethod

Collect Python environment information.

Returns:

Name Type Description
PythonInfo PythonInfo

Python environment information

Source code in src/kazunoko/doctor.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
@classmethod
def collect(cls) -> "PythonInfo":
    """
    Collect Python environment information.

    Returns:
        PythonInfo: Python environment information
    """
    # Detect virtual environment type
    venv_type = None
    if hasattr(sys, "real_prefix"):
        venv_type = "virtualenv"
    elif sys.prefix != sys.base_prefix:
        # Check for uv by looking for .venv/pyvenv.cfg with uv marker
        pyvenv_cfg = Path(sys.prefix) / "pyvenv.cfg"
        if pyvenv_cfg.exists():
            try:
                content = pyvenv_cfg.read_text()
                if "uv" in content.lower():
                    venv_type = "uv"
                else:
                    venv_type = "venv"
            except Exception:
                venv_type = "venv"
        else:
            venv_type = "venv"
    elif "CONDA_DEFAULT_ENV" in sys.prefix or "conda" in sys.prefix.lower():
        venv_type = "conda"

    return cls(
        python_version=sys.version.split()[0],  # e.g., "3.11.5"
        python_executable=sys.executable,
        python_prefix=sys.prefix,
        venv_type=venv_type,
    )

PlatformInfo

kazunoko.doctor.PlatformInfo

Platform and OS information.

Source code in src/kazunoko/doctor.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
@dataclass
class PlatformInfo:
    """Platform and OS information."""
    os_system: str = Field(..., description="Operating system")
    os_release: str = Field(..., description="OS release")
    os_version: str = Field(..., description="OS version")
    architecture: str = Field(..., description="CPU architecture")
    python_implementation: str = Field(..., description="Python implementation")

    @classmethod
    def collect(cls) -> "PlatformInfo":
        """
        Collect operating system and platform information.

        Returns:
            PlatformInfo: Platform and OS information
        """
        return cls(
            os_system=platform.system(),
            os_release=platform.release(),
            os_version=platform.version(),
            architecture=platform.machine(),
            python_implementation=platform.python_implementation(),
        )

collect() classmethod

Collect operating system and platform information.

Returns:

Name Type Description
PlatformInfo PlatformInfo

Platform and OS information

Source code in src/kazunoko/doctor.py
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
@classmethod
def collect(cls) -> "PlatformInfo":
    """
    Collect operating system and platform information.

    Returns:
        PlatformInfo: Platform and OS information
    """
    return cls(
        os_system=platform.system(),
        os_release=platform.release(),
        os_version=platform.version(),
        architecture=platform.machine(),
        python_implementation=platform.python_implementation(),
    )

DeviceInfo

kazunoko.doctor.DeviceInfo

Device connection and status information.

Source code in src/kazunoko/doctor.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
@dataclass
class DeviceInfo:
    """Device connection and status information."""
    connected_port: str = Field(..., description="Connected port name")
    firmware_version: str | None = Field(default=None, description="Firmware version")
    mac_address: str | None = Field(default=None, description="MAC address")
    status: str | None = Field(default=None, description="Device status")
    uptime_ms: int | None = Field(default=None, description="Device uptime in milliseconds")

    @classmethod
    def collect(cls, device: DeviceProtocol) -> "DeviceInfo":
        """
        Collect device information from connected device.

        Args:
            device: Connected DeviceProtocol instance

        Returns:
            DeviceInfo: Device connection and status information
        """
        try:
            cmd = Command(device)

            # Get device information from single GET_STATUS command
            # GET_STATUS returns: version, mac_address, uptime_ms, and other info
            logger.debug("Getting device status")
            status_resp = cmd.status()
            firmware_version = getattr(status_resp, "version", None)
            mac_address = getattr(status_resp, "mac_address", None)
            device_status = getattr(status_resp, "status", None)
            uptime_ms = getattr(status_resp, "uptime_ms", None)

            logger.info(
                "Device information collected",
                extra={
                    "port": device.port,
                    "firmware_version": firmware_version,
                    "mac_address": mac_address,
                    "status": device_status,
                }
            )

            return cls(
                connected_port=device.port,
                firmware_version=firmware_version,
                mac_address=mac_address,
                status=device_status,
                uptime_ms=uptime_ms,
            )

        except KazunokoError as e:
            logger.warning(
                "Failed to collect device information",
                extra={"error": str(e), "error_type": type(e).__name__},
                exc_info=True
            )
            return cls(
                connected_port=device.port if hasattr(device, "port") else "unknown",
                firmware_version=None,
                mac_address=None,
                status=None,
                uptime_ms=None,
            )

collect(device) classmethod

Collect device information from connected device.

Parameters:

Name Type Description Default
device DeviceProtocol

Connected DeviceProtocol instance

required

Returns:

Name Type Description
DeviceInfo DeviceInfo

Device connection and status information

Source code in src/kazunoko/doctor.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
@classmethod
def collect(cls, device: DeviceProtocol) -> "DeviceInfo":
    """
    Collect device information from connected device.

    Args:
        device: Connected DeviceProtocol instance

    Returns:
        DeviceInfo: Device connection and status information
    """
    try:
        cmd = Command(device)

        # Get device information from single GET_STATUS command
        # GET_STATUS returns: version, mac_address, uptime_ms, and other info
        logger.debug("Getting device status")
        status_resp = cmd.status()
        firmware_version = getattr(status_resp, "version", None)
        mac_address = getattr(status_resp, "mac_address", None)
        device_status = getattr(status_resp, "status", None)
        uptime_ms = getattr(status_resp, "uptime_ms", None)

        logger.info(
            "Device information collected",
            extra={
                "port": device.port,
                "firmware_version": firmware_version,
                "mac_address": mac_address,
                "status": device_status,
            }
        )

        return cls(
            connected_port=device.port,
            firmware_version=firmware_version,
            mac_address=mac_address,
            status=device_status,
            uptime_ms=uptime_ms,
        )

    except KazunokoError as e:
        logger.warning(
            "Failed to collect device information",
            extra={"error": str(e), "error_type": type(e).__name__},
            exc_info=True
        )
        return cls(
            connected_port=device.port if hasattr(device, "port") else "unknown",
            firmware_version=None,
            mac_address=None,
            status=None,
            uptime_ms=None,
        )