<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 async def sendmail( self, sender: str, recipients: RecipientsType, message: Union[str, bytes], mail_options: Iterable[str] = None, rcpt_options: Iterable[str] = None, timeout: DefaultNumType = _default, ) -> SendmailResponseType: """ This command performs an entire mail transaction. The arguments are: - sender: The address sending this mail. - recipients: A list of addresses to send this mail to. A bare string will be treated as a list with 1 address. - message: The message string to send. - mail_options: List of options (such as ESMTP 8bitmime) for the MAIL command. - rcpt_options: List of options (such as DSN commands) for all the RCPT commands. message must be a string containing characters in the ASCII range. The string is encoded to bytes using the ascii codec, and lone \\\\r and \\\\n characters are converted to \\\\r\\\\n characters. If there has been no previous HELO or EHLO command this session, this method tries EHLO first. This method will return normally if the mail is accepted for at least one recipient. It returns a tuple consisting of: - an error dictionary, with one entry for each recipient that was refused. Each entry contains a tuple of the SMTP error code and the accompanying error message sent by the server. - the message sent by the server in response to the DATA command (often containing a message id) Example: ({}, 'OK') (221, Bye) In the above example, the message was accepted for delivery for all three addresses. If delivery had been only successful to two of the three addresses, and one was rejected, the response would look something like:: ( {"nobody@three.org": (550, "User unknown")}, "Written safely to disk. #902487694.289148.12219.", ) If delivery is not successful to any addresses, :exc:`.SMTPRecipientsRefused` is raised. If :exc:`.SMTPResponseException` is raised by this method, we try to send an RSET command to reset the server envelope automatically for the next attempt. :raises SMTPRecipientsRefused: delivery to all recipients failed :raises SMTPResponseException: on invalid response """

        if isinstance(recipients, str):
            recipients = [recipients]
        else:
            recipients = list(recipients)

        if mail_options is None:
            mail_options = []
        else:
            mail_options = list(mail_options)

        if rcpt_options is None:
            rcpt_options = []
        else:
            rcpt_options = list(rcpt_options)

        async with self._sendmail_lock:
            if self.supports_extension("size"):
                size_option = "size={}".format(len(message))
                mail_options.append(size_option)

            try:
                await self.mail(sender, options=mail_options, timeout=timeout)
                recipient_errors = await self._send_recipients(
                    recipients, options=rcpt_options, timeout=timeout
                )
                response = await self.data(message, timeout=timeout)
            except (SMTPResponseException, SMTPRecipientsRefused) as exc:
                # If we got an error, reset the envelope.
                try:
                    await self.rset(timeout=timeout)
                except (ConnectionError, SMTPResponseException):
                    # If we're disconnected on the reset, or we get a bad
                    # status, don't raise that as it's confusing
                    pass
                raise exc

        return recipient_errors, response.message