def unpack(data: bytes, *, is_json: bool | None = None) -> tuple[Any, bool]:
        """Unpack the incoming message

        Parameters
        ----------
        data: bytes
            The incoming message to unpack
        is_json: bool | None
            If the message should be unpacked as json.  By default, try to
            unpack json and fallback gracefully to marshalled bytes.

        Returns
        -------
        tuple[Any, bool]
            A tuple of the unpacked object and a boolean denoting if the
            message was deserialized using json.  If True, the return message
            should be packed as json.
        """
        if is_json is None or is_json:
            try:
                return json.loads(data.decode()), True
            except ValueError as e:
                if is_json:
                    raise IPCError("Unable to decode json data") from e

        try:
            assert len(data) >= HDRLEN
            size = struct.unpack(HDRFORMAT, data[:HDRLEN])[0]
            assert size >= len(data[HDRLEN:])
            return marshal.loads(data[HDRLEN : HDRLEN + size]), False
        except AssertionError as e:
            raise IPCError("error reading reply! (probably the socket was disconnected)") from e
Unpack the incoming message

        Parameters
        ----------
        data: bytes
            The incoming message to unpack
        is_json: bool | None
            If the message should be unpacked as json.  By default, try to
            unpack json and fallback gracefully to marshalled bytes.

        Returns
        -------
        tuple[Any, bool]
            A tuple of the unpacked object and a boolean denoting if the
            message was deserialized using json.  If True, the return message
            should be packed as json.
        
libqtile/ipc.py