Opened 3 hours ago

#36163 new Cleanup/optimization

Change mail APIs to (mostly) keyword-only parameters

Reported by: Mike Edmunds Owned by:
Component: Core (Mail) Version: 5.1
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Several of Django's email APIs have extremely long parameter lists. This complicates adding—and documenting—new parameters where they would logically fit, as the existing positional order must be maintained for compatibility.

This ticket proposes changing the public django.core.mail APIs to require keyword-only arguments, starting at the first boolean or "uncommon" parameter. That's fail_silently for the functional APIs, and bcc for the class constructors (specifics below).

Forum discussion: https://forum.djangoproject.com/t/change-send-mail-and-emailmessage-to-kwargs-only/38239

Consensus in the forum was that this change should be made without a deprecation period. Existing code that uses positional arguments for the affected params would raise a TypeError after the change.

A quick (and by no means exhaustive) GitHub code search suggests a lot of existing code already uses keyword arguments for these params, but that positional arguments are common for the earlier ones. One potential exception is custom EmailMultiAlternatives subclasses. (More details in the forum.)

(This originally came up in the context of #35514, which must add a new provider parameter to several existing APIs—including the "frozen" send_mail() and send_mass_mail() functions, and the EmailMessage class with its documented parameter ordering.)

Proposed changes

This change would add *,s where indicated below. All parameters after the *, would become keyword only.

# django/core/mail/__init__.py
def get_connection(
    backend=None, 
    *, 
    fail_silently=False, **kwds):

def send_mail(
    subject, message, from_email, recipient_list,
    *,
    fail_silently=False, auth_user=None, auth_password=None,
    connection=None, html_message=None):

def send_mass_mail(
    datatuple, 
    *,
    fail_silently=False, auth_user=None, auth_password=None,
    connection=None):

def mail_admins(
    subject, message, 
    *,
    fail_silently=False, connection=None, html_message=None):

def mail_managers(
    subject, message,
    *,
    fail_silently=False, connection=None, html_message=None):
# django/core/mail/message.py
class EmailMessage:
    def __init__(
        self,
        subject="", body="", from_email=None, to=None,
        *,
        bcc=None, connection=None,
        attachments=None, headers=None,
        cc=None, reply_to=None):


class EmailMultiAlternatives(EmailMessage):
    def __init__(
        self,
        subject="", body="", from_email=None, to=None,
        *,
        bcc=None, connection=None,
        attachments=None, headers=None,
        alternatives=None,
        cc=None, reply_to=None):

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top