[Zope3-checkins] CVS: Zope3/src/zope/app/mail - __init__.py:1.1 configure.zcml:1.1 event.py:1.1 mail.py:1.1 mailer.py:1.1 meta.zcml:1.1 metaconfigure.py:1.1 service.py:1.1
Stephan Richter
srichter@cbu.edu
Wed, 16 Apr 2003 09:45:44 -0400
Update of /cvs-repository/Zope3/src/zope/app/mail
In directory cvs.zope.org:/tmp/cvs-serv29814/mail
Added Files:
__init__.py configure.zcml event.py mail.py mailer.py
meta.zcml metaconfigure.py service.py
Log Message:
This is a first stab at a flexible facility to send out mail. I discussed
the API with Barry a bit, before writing this code.
Goals:
- Keep the actual IMailService interface as simple as possible.
- Allow flexible mechanisms to send mail.
- Be able to implement and register you own mail sending policies.
Implementation:
- The global mail service is an asynchronous mail sender, which means
it does not block the request. Once it is done, it will send an event
to which interested parties can subscribe.
- The AsyncMailService registers and works with Mailer objects, which
encode the policy of the mailing. I supplied a SimpleMailer, which has
no specific policies and a BatchMailer, which allows to send the
messages in batches, so that the SMTP server will not overload. I
currently do not have any of the mailers in action, but I am going to
add MailService support to the zwiki product for testing purposes soon.
- MailService will make heavy use of the EventService for publishing
status updates, which is necessary due to its async nature. Currently
I have only one event, but I plan to add more soon.
To Do:
- I lied about the AsyncMailService to be async! :-) I actually have not
implemented the async behavior yet; I need help from other people for
this feature. Should I use thread or asyncore, and how do I use either
one. I am not really knowledgable in this area.
- As mentioned above, add more events.
- Come up with advanced unit tests for the BatchMailer.
- Put the MailService-specific ZCML directive into a higher up ZCML file,
since it contains server-specific information, similar to the ones for
HTTP and FTP.
=== Added File Zope3/src/zope/app/mail/__init__.py ===
=== Added File Zope3/src/zope/app/mail/configure.zcml ===
<zopeConfigure
xmlns="http://namespaces.zope.org/zope"
xmlns:service="http://namespaces.zope.org/service"
xmlns:mail="http://namespaces.zope.org/mail"
>
<serviceType id="Mail"
interface="zope.app.interfaces.mail.IMailService" />
<mail:mailservice name="Mail"
hostname="localhost" port="25"
class=".service.AsyncMailService"
permission="zope.Public"/>
<mail:mailer name="SimpleMailer" class=".mailer.SimpleMailer"
serviceType="Mail" default="True" />
<mail:mailer name="BatchMailer" class=".mailer.BatchMailer"
serviceType="Mail" />
</zopeConfigure>
=== Added File Zope3/src/zope/app/mail/event.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Collection of possible Mail Events.
$Id: event.py,v 1.1 2003/04/16 13:45:43 srichter Exp $
"""
from zope.app.interfaces.mail import IMailSentEvent
class MailSentEvent:
__doc__ = IMailSentEvent.__doc__
__implements__ = IMailSentEvent
def __init__(self, mailer):
self.mailer = mailer
=== Added File Zope3/src/zope/app/mail/mail.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""MailService Implementation
Simple implementation of the MailService, Mailers and MailEvents.
$Id: mail.py,v 1.1 2003/04/16 13:45:43 srichter Exp $
"""
from smtplib import SMTP
from zope.app.interfaces.mail import IAsyncMailService, IMailer, IMailSentEvent
from zope.app.event import publish
class AsyncMailService:
__doc__ = IAsyncMailService.__doc__
__implements__ = IAsyncMailService
# See zope.app.interfaces.services.mail.IMailService
hostname = u''
# See zope.app.interfaces.services.mail.IMailService
port = 25
# See zope.app.interfaces.services.mail.IMailService
username = None
# See zope.app.interfaces.services.mail.IMailService
password = None
def __init__(self):
"""Initialize the object."""
self.__mailers = {}
self.__default_mailer = ''
def createMailer(self, name):
"See zope.app.interfaces.services.mail.IAsyncMailService"
return self.__mailers[name]()
def getMailerNames(self):
"See zope.app.interfaces.services.mail.IAsyncMailService"
return self.__mailers.keys()
def getDefaultMailerName(self):
"See zope.app.interfaces.services.mail.IAsyncMailService"
return self.__default_mailer
def send(self, fromaddr, toaddrs, message, mailer=None):
"See zope.app.interfaces.services.mail.IMailService"
if mailer is None:
mailer = self.createMailer(self.getDefaultMailerName)
# XXX: should be called in new thread:should we use thread or async?
mailer.send(fromaddr, toaddrs, message, self.hostname, self.port,
self.username, self.password)
def provideMailer(self, name, klass, default=False):
"""Add a new mailer to the service."""
self.__mailers[name] = klass
if default:
self.__default_mailer = name
class SimpleMailer:
__doc__ = IMailer.__doc__
__implements__ = IMailer
def send(self, fromaddr, toaddrs, message,
hostname, port, username, password):
"See zope.app.interfaces.services.mail.IMailer"
server = SMTP(hostname, port)
server.set_debuglevel(0)
if username is not None and password is not None:
server.login(username, password)
server.sendmail(fromaddr, toaddrs, message)
server.quit()
publish(self, MailSentEvent(self))
class MailSentEvent:
__doc__ = IMailSentEvent.__doc__
__implements__ = IMailSentEvent
def __init__(self, mailer):
self.mailer = mailer
=== Added File Zope3/src/zope/app/mail/mailer.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A c
Simple implementation of the MailService, Mailers and MailEvents.
$Id: mailer.py,v 1.1 2003/04/16 13:45:43 srichter Exp $
"""
from smtplib import SMTP
from time import sleep
from zope.app.interfaces.mail import IMailer, IBatchMailer
from zope.app.event import publish
from zope.app.mail.event import MailSentEvent
class SimpleMailer:
__doc__ = IMailer.__doc__
__implements__ = IMailer
def send(self, fromaddr, toaddrs, message,
hostname, port, username, password):
"See zope.app.interfaces.services.mail.IMailer"
server = SMTP(hostname, port)
server.set_debuglevel(0)
if username is not None and password is not None:
server.login(username, password)
server.sendmail(fromaddr, toaddrs, message)
server.quit()
publish(self, MailSentEvent(self))
class BatchMailer:
__doc__ = IBatchMailer.__doc__
__implements__ = IBatchMailer
# See zope.app.interfaces.mail.IBatchMailer
batchDelay = 5000
# See zope.app.interfaces.mail.IBatchMailer
batchSize = 5
def send(self, fromaddr, toaddrs, message,
hostname, port, username, password):
"See zope.app.interfaces.mail.IMailer"
server = SMTP(hostname, port)
server.set_debuglevel(0)
if username is not None and password is not None:
server.login(username, password)
recv = list(toaddrs)
batch = []
while recv:
while len(batch) < self.batchSize and recv:
batch.append(recv.pop())
server.sendmail(fromaddr, batch, message)
batch = []
time.sleep(self.batchDelay/1000.0)
server.quit()
publish(self, MailSentEvent(self))
=== Added File Zope3/src/zope/app/mail/meta.zcml ===
<zopeConfigure xmlns="http://namespaces.zope.org/zope">
<directives namespace="http://namespaces.zope.org/mail">
<directive name="mailservice" handler=".metaconfigure.mailservice">
<description>
This directive creates and registers a global mail service. It should
be only called once during startup.
</description>
<attribute name="name" required="no">
<description>
Specifies the Service name of the mail service. The default is
"Mail".
</description>
</attribute>
<attribute name="permission" required="yes">
<description>
Defines the permission that is required to use this object.
</description>
</attribute>
<attribute name="class" required="yes">
<description>
Class of the Mail Service.
</description>
</attribute>
<attribute name="hostname" required="no">
<description>
Name of the server that is used to send the mail. Default is set to
'localhost'.
</description>
</attribute>
<attribute name="port" required="no">
<description>
Port on the server that is used to send the mail. Default is set to
to the standard port '25'.
</description>
</attribute>
<attribute name="username" required="no">
<description>
Some SMTP servers support authentication. If no username is given,
then the Mail Service will not try to use authentication.
</description>
</attribute>
<attribute name="password" required="no">
<description>
Password that is used for authentication. Makes only sense in
combination with username.
</description>
</attribute>
</directive>
<directive name="mailer" handler=".metaconfigure.mailer">
<description>
Registers a new mailer class wiht the global translation service.
</description>
<attribute name="name" required="yes">
<description>
Name of the mailer class under which it is registered in the global
mail service.
</description>
</attribute>
<attribute name="class" required="yes">
<description>
The class representing this object.
</description>
</attribute>
<attribute name="serviceType" required="no">
<description>
Specifies the service type for which the mailer should be
registered. The default is "Mail".
</description>
</attribute>
<attribute name="default" required="no">
<description>
Specifies whether this mailer is the default mailer. The default
value for the 'default' attribute is "False".
</description>
</attribute>
</directive>
</directives>
</zopeConfigure>
=== Added File Zope3/src/zope/app/mail/metaconfigure.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""mail ZCML Namespace handler
$Id: metaconfigure.py,v 1.1 2003/04/16 13:45:43 srichter Exp $
"""
from zope.component import getService
from zope.configuration.action import Action
from zope.app.component.metaconfigure import provideService
def mailservice(_context, class_, permission, name="Mail",
hostname="localhost", port=25, username=None, password=None):
component = _context.resolve(class_)()
component.hostname = hostname
component.port = int(port)
component.username = username
component.password = password
return [
Action(
discriminator = ('service', name),
callable = provideService,
args = (name, component, permission),
)
]
def mailer(_context, name, class_, serviceType="Mail", default=False):
klass = _context.resolve(class_)
if default == "True":
default = True
def register(serviceType, name, klass, default):
mailservice = getService(None, serviceType)
mailservice.provideMailer(name, klass, default)
return [
Action(
discriminator = ('mailer', name),
callable = register,
args = (serviceType, name, klass, default)
)
]
=== Added File Zope3/src/zope/app/mail/service.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""MailService Implementation
This module contains various implementations of MailServices.
$Id: service.py,v 1.1 2003/04/16 13:45:43 srichter Exp $
"""
from zope.app.interfaces.mail import IAsyncMailService
class AsyncMailService:
__doc__ = IAsyncMailService.__doc__
__implements__ = IAsyncMailService
# See zope.app.interfaces.services.mail.IMailService
hostname = u''
# See zope.app.interfaces.services.mail.IMailService
port = 25
# See zope.app.interfaces.services.mail.IMailService
username = None
# See zope.app.interfaces.services.mail.IMailService
password = None
def __init__(self):
"""Initialize the object."""
self.__mailers = {}
self.__default_mailer = ''
def createMailer(self, name):
"See zope.app.interfaces.services.mail.IAsyncMailService"
return self.__mailers[name]()
def getMailerNames(self):
"See zope.app.interfaces.services.mail.IAsyncMailService"
return self.__mailers.keys()
def getDefaultMailerName(self):
"See zope.app.interfaces.services.mail.IAsyncMailService"
return self.__default_mailer
def send(self, fromaddr, toaddrs, message, mailer=None):
"See zope.app.interfaces.services.mail.IMailService"
if mailer is None:
mailer = self.createMailer(self.getDefaultMailerName())
# XXX: should be called in new thread:should we use thread or async?
mailer.send(fromaddr, toaddrs, message, self.hostname, self.port,
self.username, self.password)
def provideMailer(self, name, klass, default=False):
"""Add a new mailer to the service."""
self.__mailers[name] = klass
if default:
self.__default_mailer = name