Working on Issue #182 (requires tests/doc)

This commit is contained in:
BJ Dierkes 2014-09-10 18:27:38 -05:00
parent d03ea01976
commit 6e9881bd8d
21 changed files with 695 additions and 127 deletions

View File

@ -35,7 +35,9 @@ Features:
* :issue:`119` - Added cement.utils.shell.Prompt to quickly gather user
input in several different ways.
* :issue:`182` - Added an mail interface with basic implementations.
* :issue:`182` - Added an mail interface with basic implementations of
'dummy' (just prints message to console) and 'smtp' supporting sending
email via SMTP.
* :issue:`229` - Ability to override handlers via command line options.
Also related: :issue:`225`.
* :issue:`248` - Added documentation for BASH Auto-Completion example.

View File

@ -4,12 +4,8 @@ import re
import os
import sys
import signal
from ..core import backend, exc, handler, hook, log, config, plugin, interface
from ..core import output, extension, arg, controller, meta, cache, mail
from ..ext import ext_configparser, ext_argparse, ext_logging
from ..ext import ext_nulloutput, ext_plugin
from ..utils.misc import is_true, minimal_logger
from ..utils import fs
@ -395,37 +391,37 @@ class CementApp(meta.MetaMixin):
signal_handler = cement_signal_handler
"""A function that is called to handle any caught signals."""
config_handler = ext_configparser.ConfigParserConfigHandler
config_handler = 'configparser'
"""
A handler class that implements the IConfig interface.
"""
mail_handler = mail.DummyMailHandler
mail_handler = 'dummy'
"""
A handler class that implements the IMail interface.
"""
extension_handler = extension.CementExtensionHandler
extension_handler = 'cement'
"""
A handler class that implements the IExtension interface.
"""
log_handler = ext_logging.LoggingLogHandler
log_handler = 'logging'
"""
A handler class that implements the ILog interface.
"""
plugin_handler = ext_plugin.CementPluginHandler
plugin_handler = 'cement'
"""
A handler class that implements the IPlugin interface.
"""
argument_handler = ext_argparse.ArgParseArgumentHandler
argument_handler = 'argparse'
"""
A handler class that implements the IArgument interface.
"""
output_handler = ext_nulloutput.NullOutputHandler
output_handler = 'dummy'
"""
A handler class that implements the IOutput interface.
"""
@ -466,7 +462,7 @@ class CementApp(meta.MetaMixin):
"""
core_extensions = [
'cement.ext.ext_nulloutput',
'cement.ext.ext_dummy',
'cement.ext.ext_plugin',
'cement.ext.ext_configparser',
'cement.ext.ext_logging',
@ -489,6 +485,8 @@ class CementApp(meta.MetaMixin):
'plugin_dir',
'ignore_deprecation_warnings',
'template_dir',
'cache_handler',
'mail_handler',
]
"""
List of meta options that can/will be overridden by config options
@ -552,6 +550,7 @@ class CementApp(meta.MetaMixin):
``template_dirs``.
"""
def __init__(self, label=None, **kw):
super(CementApp, self).__init__(**kw)
@ -571,6 +570,7 @@ class CementApp(meta.MetaMixin):
self.output = None
self.controller = None
self.cache = None
self.mail = None
# setup argv... this has to happen before lay_cement()
if self._meta.argv is None:
@ -949,6 +949,7 @@ class CementApp(meta.MetaMixin):
# override select Meta via config
base_dict = self.config.get_section_dict(self._meta.config_section)
for key in base_dict:
if key in self._meta.core_meta_override or \
key in self._meta.meta_override:
@ -958,6 +959,31 @@ class CementApp(meta.MetaMixin):
else:
setattr(self._meta, key, base_dict[key])
# load extensions from configuraton file
if 'extensions' in self.config.keys(self._meta.label):
exts = self.config.get(self._meta.label, 'extensions')
# convert a comma-separated string to a list
if type(exts) is str:
ext_list = exts.split(',')
# clean up extra space if they had it inbetween commas
ext_list = [x.strip() for x in ext_list]
# set the new extensions value in the config
self.config.set(self._meta.label, 'extensions', ext_list)
# otherwise, if it's a list (ConfigObj?)
elif type(exts) is list:
ext_list = exts
for ext in ext_list:
# load the extension
self.ext.load_extension(ext)
# add to meta data
self._meta.extensions.append(ext)
def _setup_mail_handler(self):
LOG.debug("setting up %s.mail handler" % self._meta.label)
self.mail = self._resolve_handler('mail',
@ -1096,6 +1122,8 @@ class CementApp(meta.MetaMixin):
label = 'myapp'
def validate_config(self):
super(MyApp, self).validate_config()
# test that the log file directory exist, if not create it
logdir = os.path.dirname(self.config.get('log', 'file'))

View File

@ -14,7 +14,7 @@ def mail_validator(klass, obj):
'send',
]
### FIX ME: Validate Configuration Defaults Here
### FIX ME: Validate Meta/Configuration Defaults Here
interface.validate(IMail, obj, members)
@ -27,7 +27,21 @@ class IMail(interface.Interface):
Implementations do *not* subclass from interfaces.
Usage:
**Configuration**
Implementations much support the following configuration settings:
* **to** - Default ``to`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **from_addr** - Default ``from_addr`` address
* **cc** - Default ``cc`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **bcc** - Default ``bcc`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **subject** - Default ``subject``
* **subject_prefix** - Additional string to prepend to the ``subject``
**Usage**
.. code-block:: python
@ -67,20 +81,39 @@ class IMail(interface.Interface):
def send(body, **kwargs):
"""
Send a mail message. Keyword arguments override defaults defined
in the handler implementations configuration (see possible keyword
arguments below).
Send a mail message. Keyword arguments override configuration
defaults (cc, bcc, etc).
:param body: The message body to send
:type body: Multiline string
:type body: multiline string
:keyword to: List of recipients (generally email addresses)
:type to: list
:keyword from_addr: Address (generally email) of the sender
:type from_addr: string
:keyword cc: List of CC Recipients
:type cc: list
:keyword bcc: List of BCC Recipients
:keyword from: Address (generall email) of the sender
:type bcc: list
:keyword subject: Message subject line
:type subject: string
:returns: Boolean (``True`` if message is sent successfully, ``False``
otherwise)
**Usage**
.. code-block:: python
# Using all configuration defaults
app.send('This is my message body')
# Overriding configuration defaults
app.send('My message body'
to=['john@example.com'],
from_addr='me@example.com',
cc=['jane@example.com', 'rita@example.com'],
subject='This is my subject',
)
"""
@ -88,6 +121,10 @@ class CementMailHandler(handler.CementBaseHandler):
"""
Base class that all Mail Handlers should sub-class from.
**Configuration Options**
This handler supports the following configuration options under a
"""
class Meta:
"""
@ -104,7 +141,7 @@ class CementMailHandler(handler.CementBaseHandler):
#: Configuration default values
config_defaults = {
'to' : [],
'from' : 'noreply@example.com',
'from_addr' : 'noreply@example.com',
'cc' : [],
'bcc' : [],
'subject' : 'Default Subject Line',
@ -132,47 +169,10 @@ class CementMailHandler(handler.CementBaseHandler):
value_list = value.split(',')
# clean up extra space if they had it inbetween commas
value_list = (x.strip() for x in value_list)
value_list = [x.strip() for x in value_list]
# set the new extensions value in the config
self.app.config.set(self._meta.config_section, item,
value_list)
class DummyMailHandler(CementMailHandler):
class Meta:
label = 'dummy'
def _get_params(self, **kw):
params = dict()
for item in ['to', 'from', 'cc', 'bcc', 'subject', 'subject_prefix']:
config_item = self.app.config.get(self._meta.config_section, item)
params[item] = getattr(kw, item, config_item)
return params
def send(self, body, **kw):
# shorted config values
params = self._get_params(**kw)
msg = "\n" + "=" * 77 + "\n"
msg += "DUMMY MAIL MESSAGE\n"
msg += "-" * 77 + "\n\n"
msg += "To: %s\n" % ', '.join(params['to'])
msg += "From: %s\n" % params['from']
msg += "CC: %s\n" % ', '.join(params['cc'])
msg += "BCC: %s\n" % ', '.join(params['bcc'])
msg += "Subject: %s%s\n\n---\n\n" % (params['subject_prefix'],
params['subject'])
msg += body + "\n"
msg += "\n" + "-" * 77 + "\n"
print msg
class SMTPMailHandler(CementMailHandler):
class Meta:
label = 'smtp'
def send(self, body, **kw):
pass

222
cement/ext/ext_dummy.py Normal file
View File

@ -0,0 +1,222 @@
"""Dummy Framework Extension"""
from ..core import backend, output, handler, mail
from ..utils.misc import minimal_logger
LOG = minimal_logger(__name__)
class DummyOutputHandler(output.CementOutputHandler):
"""
This class is an internal implementation of the
:ref:`IOutput <cement.core.output>` interface. It does not take any
parameters on initialization, and does not actually output anything.
"""
class Meta:
"""Handler meta-data"""
interface = output.IOutput
"""The interface this class implements."""
label = 'dummy'
"""The string identifier of this handler."""
display_override_option = False
def render(self, data_dict, template=None):
"""
This implementation does not actually render anything to output, but
rather logs it to the debug facility.
:param data_dict: The data dictionary to render.
:param template: The template parameter is not used by this
implementation at all.
:returns: None
"""
LOG.debug("not rendering any output to console")
LOG.debug("DATA: %s" % data_dict)
return None
class DummyMailHandler(mail.CementMailHandler):
"""
This class implements the :ref:`IMail <cement.core.mail>`
interface, but is intended for use in development as no email is actually
sent.
**Usage**
.. code-block:: python
class MyApp(CementApp):
class Meta:
label = 'myapp'
mail_handler = 'dummy'
# create, setup, and run the app
app = MyApp()
app.setup()
app.run()
# fake sending an email message
app.mail.send('This is my fake message',
subject='This is my subject',
to=['john@example.com', 'rita@example.com'],
from_addr='me@example.com',
)
The above will print the following to console:
.. code-block:: text
======================================================================
DUMMY MAIL MESSAGE
----------------------------------------------------------------------
To: john@example.com, rita@example.com
From: me@example.com
CC:
BCC:
Subject: This is my subject
---
This is my fake message
----------------------------------------------------------------------
**Configuration**
This handler supports the following configuration settings:
* **to** - Default ``to`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **from_addr** - Default ``from_addr`` address
* **cc** - Default ``cc`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **bcc** - Default ``bcc`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **subject** - Default ``subject``
* **subject_prefix** - Additional string to prepend to the ``subject``
You can add these to any application configuration file under a
``[mail.dummy]`` section, for example:
**~/.myapp.conf**
.. code-block:: text
[myapp]
# set the mail handler to use
mail_handler = dummy
[mail.dummy]
# default to addresses (comma separated list)
to = me@example.com
# default from address
from = someone_else@example.com
# default cc addresses (comma separated list)
cc = jane@example.com, rita@example.com
# default bcc addresses (comma separated list)
bcc = blackhole@example.com, someone_else@example.com
# default subject
subject = This is The Default Subject
# additional prefix to prepend to the subject
subject_prefix = MY PREFIX >
"""
class Meta:
#: Unique identifier for this handler
label = 'dummy'
def _get_params(self, **kw):
params = dict()
for item in ['to', 'from_addr', 'cc', 'bcc', 'subject']:
config_item = self.app.config.get(self._meta.config_section, item)
params[item] = kw.get(item, config_item)
# also grab the subject_prefix
params['subject_prefix'] = self.app.config.get(
self._meta.config_section,
'subject_prefix'
)
return params
def send(self, body, **kw):
"""
Mimic sending an email message, but really just print what would be
sent to console. Keyword arguments override configuration
defaults (cc, bcc, etc).
:param body: The message body to send
:type body: multiline string
:keyword to: List of recipients (generally email addresses)
:type to: list
:keyword from_addr: Address (generally email) of the sender
:type from_addr: string
:keyword cc: List of CC Recipients
:type cc: list
:keyword bcc: List of BCC Recipients
:type bcc: list
:keyword subject: Message subject line
:type subject: string
:returns: Boolean (``True`` if message is sent successfully, ``False``
otherwise)
**Usage**
.. code-block:: python
# Using all configuration defaults
app.send('This is my message body')
# Overriding configuration defaults
app.send('My message body'
to=['john@example.com'],
from_addr='me@example.com',
cc=['jane@example.com', 'rita@example.com'],
subject='This is my subject',
)
"""
# shorted config values
params = self._get_params(**kw)
msg = "\n" + "=" * 77 + "\n"
msg += "DUMMY MAIL MESSAGE\n"
msg += "-" * 77 + "\n\n"
msg += "To: %s\n" % ', '.join(params['to'])
msg += "From: %s\n" % params['from_addr']
msg += "CC: %s\n" % ', '.join(params['cc'])
msg += "BCC: %s\n" % ', '.join(params['bcc'])
if params['subject_prefix'] not in [None, '']:
msg += "Subject: %s %s\n\n---\n\n" % (params['subject_prefix'],
params['subject'])
else:
msg += "Subject: %s\n\n---\n\n" % params['subject']
msg += body + "\n"
msg += "\n" + "-" * 77 + "\n"
print msg
def load(app):
handler.register(DummyOutputHandler)
handler.register(DummyMailHandler)

View File

@ -53,6 +53,12 @@ class MemcachedCacheHandler(cache.CementCacheHandler):
.. code-block:: text
[myapp]
# set the cache handler to use
cache_handler = memcached
[cache.memcached]
# time in seconds that an item in the cache will expire

View File

@ -1,46 +0,0 @@
"""NullOutput Framework Extension"""
from ..core import backend, output, handler
from ..utils.misc import minimal_logger
LOG = minimal_logger(__name__)
class NullOutputHandler(output.CementOutputHandler):
"""
This class is an internal implementation of the
:ref:`IOutput <cement.core.output>` interface. It does not take any
parameters on initialization.
"""
class Meta:
"""Handler meta-data"""
interface = output.IOutput
"""The interface this class implements."""
label = 'null'
"""The string identifier of this handler."""
display_override_option = False
def render(self, data_dict, template=None):
"""
This implementation does not actually render anything to output, but
rather logs it to the debug facility.
:param data_dict: The data dictionary to render.
:param template: The template parameter is not used by this
implementation at all.
:returns: None
"""
LOG.debug("not rendering any output to console")
LOG.debug("DATA: %s" % data_dict)
return None
def load(app):
handler.register(NullOutputHandler)

238
cement/ext/ext_smtp.py Normal file
View File

@ -0,0 +1,238 @@
import smtplib
from ..core import handler, mail
from ..utils.misc import minimal_logger, is_true
LOG = minimal_logger(__name__)
class SMTPMailHandler(mail.CementMailHandler):
"""
This class implements the :ref:`IMail <cement.core.mail>`
interface, and is based on the `smtplib
<http://docs.python.org/dev/library/smtplib.html>`_ standard library.
**Usage**
.. code-block:: python
class MyApp(CementApp):
class Meta:
label = 'myapp'
mail_handler = 'smtp'
# create, setup, and run the app
app = MyApp()
app.setup()
app.run()
# fake sending an email message
app.mail.send('This is my fake message',
subject='This is my subject',
to=['john@example.com', 'rita@example.com'],
from_addr='me@example.com',
)
**Configuration**
This handler supports the following configuration settings:
* **to** - Default ``to`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **from_addr** - Default ``from_addr`` address
* **cc** - Default ``cc`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **bcc** - Default ``bcc`` addresses (list, or comma separated depending
on the ConfigHandler in use)
* **subject** - Default ``subject``
* **subject_prefix** - Additional string to prepend to the ``subject``
* **host** - The SMTP host server
* **port** - The SMTP host server port
* **timeout** - The timeout in seconds before terminating a connection
* **ssl** - Whether to initiate SSL or not
* **tls** - Whether to use TLS or not (requires SSL)
* **auth** - Whether or not to initiate SMTP authentication
* **username** - SMTP authentication username
* **password** - SMTP authentication password
You can add these to any application configuration file under a
``[mail.smtp]`` section, for example:
**~/.myapp.conf**
.. code-block:: text
[myapp]
# set the mail handler to use
mail_handler = smtp
[mail.smtp]
# default to addresses (comma separated list)
to = me@example.com
# default from address
from = someone_else@example.com
# default cc addresses (comma separated list)
cc = jane@example.com, rita@example.com
# default bcc addresses (comma separated list)
bcc = blackhole@example.com, someone_else@example.com
# default subject
subject = This is The Default Subject
# additional prefix to prepend to the subject
subject_prefix = MY PREFIX >
# smtp host server
host = localhost
# smtp host port
port = 465
# timeout in seconds
timeout = 30
# whether or not to establish an ssl connection
ssl = 1
# whether or not to use start tls
tls = 1
# whether or not to initiate smtp auth
auth = 1
# smtp auth username
username = john.doe
# smtp auth password
password = oober_secure_password
"""
class Meta:
#: Unique identifier for this handler
label = 'smtp'
#: Configuration default values
config_defaults = {
'to' : [],
'from_addr' : 'noreply@localhost',
'cc' : [],
'bcc' : [],
'subject' : None,
'subject_prefix' : None,
'host' : 'localhost',
'port' : '25',
'timeout' : 30,
'ssl' : False,
'tls' : False,
'auth' : False,
'username' : None,
'password' : None,
}
def _get_params(self, **kw):
params = dict()
# some keyword args override configuration defaults
for item in ['to', 'from_addr', 'cc', 'bcc', 'subject']:
config_item = self.app.config.get(self._meta.config_section, item)
params[item] = kw.get(item, config_item)
# others don't
other_params = ['ssl', 'tls', 'host', 'port', 'auth', 'username',
'password', 'timeout']
for item in other_params:
params[item] = self.app.config.get(self._meta.config_section,
item)
# also grab the subject_prefix
params['subject_prefix'] = self.app.config.get(
self._meta.config_section,
'subject_prefix'
)
return params
def send(self, body, **kw):
"""
Send an email message via SMTP. Keyword arguments override
configuration defaults (cc, bcc, etc).
:param body: The message body to send
:type body: multiline string
:keyword to: List of recipients (generally email addresses)
:type to: list
:keyword from_addr: Address (generally email) of the sender
:type from_addr: string
:keyword cc: List of CC Recipients
:type cc: list
:keyword bcc: List of BCC Recipients
:type bcc: list
:keyword subject: Message subject line
:type subject: string
:returns: Boolean (``True`` if message is sent successfully, ``False``
otherwise)
**Usage**
.. code-block:: python
# Using all configuration defaults
app.send('This is my message body')
# Overriding configuration defaults
app.send('My message body'
from_addr='me@example.com',
to=['john@example.com'],
cc=['jane@example.com', 'rita@example.com'],
subject='This is my subject',
)
"""
params = self._get_params(**kw)
if is_true(params['ssl']):
server = smtplib.SMTP_SSL(params['host'], params['port'],
params['timeout'])
LOG.debug("%s : initiating ssl" % self._meta.label)
if is_true(params['tls']):
LOG.debug("%s : initiating tls" % self._meta.label)
server.starttls()
else:
server = smtplib.SMTP(params['host'], params['port'],
params['timeout'])
if is_true(params['auth']):
server.login(params['username'], params['password'])
if self.app.debug is True:
server.set_debuglevel(9)
msg = ""
msg += "From: %s\r\nTo: %s\r\n" % (params['from_addr'],
', '.join(params['to']))
msg += "Cc: %s\r\n" % ', '.join(params['cc'])
msg += "Bcc: %s\r\n" % ', '.join(params['bcc'])
if params['subject_prefix'] not in [None, '']:
msg += "Subject: %s %s\r\n\r\n" % (params['subject_prefix'],
params['subject'])
else:
msg += "Subject: %s\r\n\r\n" % params['subject']
msg += body + "\n"
server.sendmail(params['from_addr'],
params['to']+params['cc']+params['bcc'],
msg)
server.quit()
def load(app):
handler.register(SMTPMailHandler)

View File

@ -3,6 +3,7 @@
import unittest
from tempfile import mkstemp, mkdtemp
from ..core import backend, foundation
from ..utils.misc import rando
# shortcuts
from nose import SkipTest
@ -19,7 +20,7 @@ class TestApp(foundation.CementApp):
"""
class Meta:
label = 'test'
label = "app-%s" % rando()[:12]
config_files = []
argv = []
base_controller = None

View File

@ -0,0 +1,9 @@
.. _cement.core.mail:
:mod:`cement.core.mail`
--------------------------
.. automodule:: cement.core.mail
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,8 @@
.. _cement.ext.ext_dummy:
:mod:`cement.ext.ext_dummy`
------------------------------------
.. automodule:: cement.ext.ext_dummy
:members:
:show-inheritance:

View File

@ -1,8 +0,0 @@
.. _cement.ext.ext_nulloutput:
:mod:`cement.ext.ext_nulloutput`
------------------------------------
.. automodule:: cement.ext.ext_nulloutput
:members:
:show-inheritance:

View File

@ -0,0 +1,8 @@
.. _cement.ext.ext_smtp:
:mod:`cement.ext.ext_smtp`
==========================
.. automodule:: cement.ext.ext_smtp
:members:
:show-inheritance:

View File

@ -21,6 +21,7 @@ Cement Core Modules
core/hook
core/interface
core/log
core/mail
core/meta
core/output
core/plugin
@ -56,7 +57,8 @@ Cement Extension Modules
ext/ext_logging
ext/ext_memcached
ext/ext_mustache
ext/ext_nulloutput
ext/ext_dummy
ext/ext_plugin
ext/ext_smtp
ext/ext_yaml
ext/ext_yaml_configobj

View File

@ -110,8 +110,8 @@ debugging issues:
2012-07-13 02:19:42,272 (DEBUG) cement.core.foundation : adding signal handler for signal 15
2012-07-13 02:19:42,273 (DEBUG) cement.core.foundation : adding signal handler for signal 2
2012-07-13 02:19:42,273 (DEBUG) cement.core.foundation : setting up myapp.extension handler
2012-07-13 02:19:42,273 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_nulloutput' framework extension
2012-07-13 02:19:42,273 (DEBUG) cement.core.handler : registering handler '<class 'cement.ext.ext_nulloutput.NullOutputHandler'>' into handlers['output']['null']
2012-07-13 02:19:42,273 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_dummy' framework extension
2012-07-13 02:19:42,273 (DEBUG) cement.core.handler : registering handler '<class 'cement.ext.ext_dummy.DummyOutputHandler'>' into handlers['output']['null']
2012-07-13 02:19:42,273 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_plugin' framework extension
2012-07-13 02:19:42,273 (DEBUG) cement.core.handler : registering handler '<class 'cement.ext.ext_plugin.CementPluginHandler'>' into handlers['plugin']['cement']
2012-07-13 02:19:42,273 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_configparser' framework extension

View File

@ -4,7 +4,7 @@ Output Handling
===============
Cement defines an output interface called :ref:`IOutput <cement.core.output>`,
as well as the default :ref:`NullOutputHandler <cement.ext.ext_nulloutput>`
as well as the default :ref:`DummyOutputHandler <cement.ext.ext_dummy>`
that implements the interface. This handler is part of Cement, and actually
does nothing to produce output. Therefore it can be said that by default
a Cement application does not handle rendering output to the console, but
@ -16,7 +16,7 @@ interface and not the full capabilities of the implementation.
The following output handlers are included and maintained with Cement:
* :ref:`NullOutputHandler <cement.ext.ext_nulloutput>`
* :ref:`DummyOutputHandler <cement.ext.ext_dummy>`
* :ref:`JsonOutputHandler <cement.ext.ext_json>`
* :ref:`YamlOutputHandler <cement.ext.ext_yaml>`
* :ref:`GenshiOutputHandler <cement.ext.ext_genshi>`

View File

@ -86,8 +86,8 @@ Oh nice, ok... ArgParse is already setup with a few options I see. What else?
2014-04-15 12:28:24,706 (DEBUG) cement.core.foundation : adding signal handler for signal 15
2014-04-15 12:28:24,712 (DEBUG) cement.core.foundation : adding signal handler for signal 2
2014-04-15 12:28:24,712 (DEBUG) cement.core.foundation : setting up helloworld.extension handler
2014-04-15 12:28:24,712 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_nulloutput' framework extension
2014-04-15 12:28:24,712 (DEBUG) cement.core.handler : registering handler '<class 'cement.ext.ext_nulloutput.NullOutputHandler'>' into handlers['output']['null']
2014-04-15 12:28:24,712 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_dummy' framework extension
2014-04-15 12:28:24,712 (DEBUG) cement.core.handler : registering handler '<class 'cement.ext.ext_dummy.DummyOutputHandler'>' into handlers['output']['null']
2014-04-15 12:28:24,712 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_plugin' framework extension
2014-04-15 12:28:24,713 (DEBUG) cement.core.handler : registering handler '<class 'cement.ext.ext_plugin.CementPluginHandler'>' into handlers['plugin']['cement']
2014-04-15 12:28:24,713 (DEBUG) cement.core.extension : loading the 'cement.ext.ext_configparser' framework extension

View File

@ -2,6 +2,10 @@
from cement.core import exc, controller, handler
from cement.utils import test
from cement.utils.misc import rando, init_defaults
APP = "app-%s" % rando()[:12]
class TestController(controller.CementBaseController):
class Meta:
@ -225,3 +229,40 @@ class ControllerTestCase(test.CementCoreTestCase):
app.setup()
app.run()
self.eq(app.get_last_rendered()[0]['foo'], 'mypositional')
def test_load_extensions_from_config_list(self):
defaults = init_defaults(APP)
defaults[APP]['extensions'] = ['json', 'yaml']
app = self.make_app(
label=APP,
extensions=[],
config_defaults=defaults,
)
app.setup()
app.run()
res = 'cement.ext.ext_json' in app.ext._loaded_extensions
self.ok(res)
res = 'cement.ext.ext_yaml' in app.ext._loaded_extensions
self.ok(res)
def test_load_extensions_from_config_str(self):
defaults = init_defaults(APP)
defaults[APP]['extensions'] = 'json, yaml'
app = self.make_app(
label=APP,
extensions=[],
config_defaults=defaults,
)
app.setup()
app.run()
res = 'cement.ext.ext_json' in app.ext._loaded_extensions
self.ok(res)
res = 'cement.ext.ext_yaml' in app.ext._loaded_extensions
self.ok(res)

View File

@ -90,7 +90,7 @@ class FoundationTestCase(test.CementCoreTestCase):
from cement.ext import ext_logging
from cement.ext import ext_argparse
from cement.ext import ext_plugin
from cement.ext import ext_nulloutput
from cement.ext import ext_dummy
# forces CementApp._resolve_handler to register the handler
from cement.ext import ext_json
@ -102,6 +102,7 @@ class FoundationTestCase(test.CementCoreTestCase):
extension_handler=extension.CementExtensionHandler(),
plugin_handler=ext_plugin.CementPluginHandler(),
output_handler=ext_json.JsonOutputHandler(),
mail_handler=ext_dummy.DummyMailHandler(),
argv=[__file__, '--debug']
)
@ -338,3 +339,10 @@ class FoundationTestCase(test.CementCoreTestCase):
app.setup()
app._suppress_output()
def test_core_meta_override(self):
defaults = init_defaults(APP)
defaults[APP]['mail_handler'] = 'dummy'
app = self.make_app(APP, debug=True, config_defaults=defaults)
app.setup()
app.run()

View File

@ -26,7 +26,7 @@ class BogusHandler4(meta.MetaMixin):
class DuplicateHandler(output.CementOutputHandler):
class Meta:
interface = output.IOutput
label = 'null'
label = 'dummy'
def _setup(self, config_obj):
pass
@ -72,8 +72,8 @@ class HandlerTestCase(test.CementCoreTestCase):
@test.raises(exc.FrameworkError)
def test_register_duplicate_handler(self):
from cement.ext import ext_nulloutput
handler.register(ext_nulloutput.NullOutputHandler)
from cement.ext import ext_dummy
handler.register(ext_dummy.DummyOutputHandler)
try:
handler.register(DuplicateHandler)
except exc.FrameworkError:
@ -89,7 +89,7 @@ class HandlerTestCase(test.CementCoreTestCase):
def test_verify_handler(self):
self.app.setup()
self.ok(handler.registered('output', 'null'))
self.ok(handler.registered('output', 'dummy'))
self.eq(handler.registered('output', 'bogus_handler'), False)
self.eq(handler.registered('bogus_type', 'bogus_handler'), False)
@ -148,7 +148,7 @@ class HandlerTestCase(test.CementCoreTestCase):
def test_handler_registered(self):
self.app.setup()
self.eq(handler.registered('output', 'null'), True)
self.eq(handler.registered('output', 'dummy'), True)
def test_handler_get_fallback(self):
self.app.setup()

13
tests/ext/dummy_tests.py Normal file
View File

@ -0,0 +1,13 @@
"""Tests for cement.ext.ext_dummy."""
from cement.utils import test
from cement.utils.misc import rando
APP = "app-%s" % rando()[:12]
class DummyOutputHandlerTestCase(test.CementTestCase):
pass
class DummyMailHandlerTestCase(test.CementTestCase):
pass

36
tests/ext/smtp_tests.py Normal file
View File

@ -0,0 +1,36 @@
"""Tests for cement.ext.ext_smtp."""
from cement.utils import test
from cement.utils.misc import rando, init_defaults
APP = "app-%s" % rando()[:12]
class SMTPMailHandlerTestCase(test.CementTestCase):
def setUp(self):
super(SMTPMailHandlerTestCase, self).setUp()
self.app = self.make_app(APP,
extensions=['smtp'],
mail_handler='smtp',
argv=[],
)
def test_smtp_defaults(self):
defaults = init_defaults(APP, 'mail.smtp')
defaults['mail.smtp']['to'] = 'nobody@localhost'
defaults['mail.smtp']['from_addr'] = 'nobody@localhost'
defaults['mail.smtp']['cc'] = 'nobody@localhost'
defaults['mail.smtp']['bcc'] = 'nobody@localhost'
defaults['mail.smtp']['subject'] = 'Test Email To nobody@localhost'
defaults['mail.smtp']['subject_prefix'] = 'PREFIX >'
self.app = self.make_app(APP,
config_defaults=defaults,
extensions=['smtp'],
mail_handler='smtp',
argv=[],
)
self.app.setup()
self.app.run()
self.app.mail.send('TEST MESSAGE')