This decorator is used to turn an e function into an email sending function!

    The name parameter is the name of the email we're going to be sending (used to
    locate the templates on the file system).

    The allow_unverified kwarg flags whether we will send this email to an unverified
    email or not. We generally do not want to do this, but some emails are important
    enough or have special requirements that require it.

    Functions that are decorated by this need to accept two positional arguments, the
    first argument is the Pyramid request object, and the second argument is either
    a single User, or a list of Users. These users represent the recipients of this
    email. Additional keyword arguments are supported, but are not otherwise restricted.

    Functions decorated by this must return a mapping of context variables that will
    ultimately be returned, but which will also be used to render the templates for
    the emails.

    Thus this function can decorate functions with a signature like so:

        def foo(
            request: Request, user_or_users: Union[User, List[User]]
        ) -> Mapping[str, Any]:
            ...

    Finally, if the email needs to be sent to an address *other* than the user's primary
    email address, instead of a User object, a tuple of (User, Email) objects may be
    used in place of a User object.

def _email(name, *, allow_unverified=False):
    """
    This decorator is used to turn an e function into an email sending function!

    The name parameter is the name of the email we're going to be sending (used to
    locate the templates on the file system).

    The allow_unverified kwarg flags whether we will send this email to an unverified
    email or not. We generally do not want to do this, but some emails are important
    enough or have special requirements that require it.

    Functions that are decorated by this need to accept two positional arguments, the
    first argument is the Pyramid request object, and the second argument is either
    a single User, or a list of Users. These users represent the recipients of this
    email. Additional keyword arguments are supported, but are not otherwise restricted.

    Functions decorated by this must return a mapping of context variables that will
    ultimately be returned, but which will also be used to render the templates for
    the emails.

    Thus this function can decorate functions with a signature like so:

        def foo(
            request: Request, user_or_users: Union[User, List[User]]
        ) -> Mapping[str, Any]:
            ...

    Finally, if the email needs to be sent to an address *other* than the user's primary
    email address, instead of a User object, a tuple of (User, Email) objects may be
    used in place of a User object.
    """

    def inner(fn):
        @functools.wraps(fn)
        def wrapper(request, user_or_users, **kwargs):
            if isinstance(user_or_users, (list, set)):
                recipients = user_or_users
            else:
                recipients = [user_or_users]

            context = fn(request, user_or_users, **kwargs)
            msg = EmailMessage.from_template(name, context, request=request)

            for recipient in recipients:
                if isinstance(recipient, tuple):
                    user, email = recipient
                else:
                    user, email = recipient, None

                _send_email_to_user(
                    request, user, msg, email=email, allow_unverified=allow_unverified
                )

            return context

        return wrapper

    return inner