summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2010-12-22 16:57:23 -0500
committerChris McDonough <chrism@plope.com>2010-12-22 16:57:23 -0500
commit6f6d3649b280d3585d4c48541cac14e3959f9776 (patch)
tree59f83c6c1fd7259ddc40799ea1087d7aaee89843
parentd9419fb90c161b9b05e87bca175909a28d93cc28 (diff)
downloadpyramid-6f6d3649b280d3585d4c48541cac14e3959f9776.tar.gz
pyramid-6f6d3649b280d3585d4c48541cac14e3959f9776.tar.bz2
pyramid-6f6d3649b280d3585d4c48541cac14e3959f9776.zip
change flash API as per email convo with Ben and Mike
-rw-r--r--docs/api/interfaces.rst1
-rw-r--r--docs/narr/flash.rst126
-rw-r--r--pyramid/flash.py32
-rw-r--r--pyramid/interfaces.py57
-rw-r--r--pyramid/session.py19
-rw-r--r--pyramid/tests/test_flash.py81
-rw-r--r--pyramid/tests/test_session.py69
7 files changed, 117 insertions, 268 deletions
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index dab64ba15..b3c14e5f7 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -35,4 +35,3 @@ Other Interfaces
.. autointerface:: ITemplateRenderer
- .. autointerface:: IFlashMessages
diff --git a/docs/narr/flash.rst b/docs/narr/flash.rst
index 331793533..d41c2cdaf 100644
--- a/docs/narr/flash.rst
+++ b/docs/narr/flash.rst
@@ -11,113 +11,97 @@ factory` as described in :ref:`using_the_default_session_factory` or
Flash messaging has two main uses: to display a status message only once to
the user after performing an internal redirect, and to allow generic code to
log messages for single-time display without having direct access to an HTML
-template. The user interface consists of two methods of the :term:`session`
-object.
+template. The user interface consists of a number of methods of the
+:term:`session` object.
Using the ``session.flash`` Method
----------------------------------
-To add a message to a flash queue, use a session object's ``flash`` method:
+To add a message to a flash message queue, use a session object's ``flash``
+method:
.. code-block:: python
:linenos:
request.session.flash('mymessage')
-The ``.flash`` method appends a message to the queue, creating the queue if
-necessary. The message is not modified in any way.
+The ``.flash`` method appends a message to a flash queue, creating the queue
+if necessary.
-The ``category`` argument names a category or level. The library defines
-several default category names: ``debug``, ``info``, ``success``, ``warning``
-and ``error``. The default category level is ``info``.
+``.flash`` accepts three arguments:
-The ``queue_name`` argument allows you to define multiple message
-queues. This can be used to display different kinds of messages in different
-places on a page. You cam pass any name for your queue, but it must be a
-string. The default value is the empty string, which chooses the default
-queue. Each queue is independent, and can be popped by ``unflash``
-separately.
+.. method:: flash(message, queue='', allow_duplicate=True)
-Constant names for flash message category names are importable from the
-:mod:`pyramid.flash` module as ``DEBUG``, ``INFO``, ``SUCCESS``, ``WARNING``
-and ``ERROR``, which respectively name ``debug``, ``info``, ``success``,
-``warning`` and ``error`` strings. For example you can do this:
+The ``message`` argument is required. It represents a message you wish to
+later display to a user. It is usually a string but the ``message`` you
+provide is not modified in any way.
-.. code-block:: python
+The ``queue`` argument allows you to choose a queue to which to append the
+message you provide. This can be used to push different kinds of messages
+into flash storage for later display in different places on a page. You cam
+pass any name for your queue, but it must be a string. The default value is
+the empty string, which chooses the default queue. Each queue is independent,
+and can be popped by ``pop_flash`` or examined via ``peek_flash`` separately.
+``queue`` defaults to the empty string. The empty string represents the
+default flash message queue.
- from pyramid import flash
- request.session.flash(msg, flash.DEBUG)
+.. code-block:: python
-Or you can use the literal name ``debug``:
+ request.session.flash(msg, 'myappsqueue')
-.. code-block:: python
+The ``allow_duplicate`` argument, which defaults to ``True``. If this is
+``False``, if you attempt to add a message to a queue which is already
+present in the queue, it will not be added.
- request.session.flash(msg, 'debug')
+Using the ``session.pop_flash`` Method
+--------------------------------------
-Both examples do the same thing. The meanings of flash category names are
-detailed in :mod:`pyramid.flash`.
+Once one or more messages has been added to a flash queue by the
+``session.flash`` API, the ``session.pop_flash`` API can be used to pop that
+queue and return it for use.
To pop a particular queue of messages from the flash object, use the session
-object's ``unflash`` method.
+object's ``pop_flash`` method.
.. code-block:: python
:linenos:
- >>> request.session.flash('info message', 'info')
- >>> messages = request.session.unflash()
- >>> messages['info']
+ >>> request.session.flash('info message')
+ >>> request.session.pop_flash()
['info message']
-Using the ``session.unflash`` Method
-------------------------------------
-
-Once one or more messages has been added to a flash queue by the
-``session.flash`` API, the ``session.unflash`` API can be used to pop that
-queue and return it for use.
-
-For example some code that runs in a view callable might call the
-``session.flash`` API:
+Calling ``session.pop_flash()`` again like above without a corresponding call
+to ``session.flash`` will return an empty list, because the queue has already
+been popped.
.. code-block:: python
:linenos:
- request.session.flash('mymessage')
-
-A corresponding ``session.unflash`` might be called on a subsequent request:
-
-.. code-block:: python
- :linenos:
+ >>> request.session.flash('info message')
+ >>> request.session.pop_flash()
+ ['info message']
+ >>> request.session.pop_flash()
+ []
- messages = request.session.unflash()
+The object returned from ``pop_flash`` is a list.
-Calling ``session.unflash`` again like above without a corresponding call to
-``session.flash`` will return an empty ``messages`` object, because the queue
-has already been popped.
+Using the ``session.pop_flash`` Method
+--------------------------------------
-The ``messages`` object returned from ``unflash`` is a dictionary-like
-object. Its keys are category names, and its values are sequences of
-strings. For ease of use, the dict-like object returned by ``unflash`` isn't
-a "plain" dict: it's an object which has several helper methods, each named
-after a particular flash category level. These methods return all messages
-related to the category name:
+Once one or more messages has been added to a flash queue by the
+``session.flash`` API, the ``session.peek_flash`` API can be used to "peek"
+at that queue. Unlike ``session.pop_flash``, the queue is not popped from
+flash storage.
.. code-block:: python
:linenos:
- >>> request.session.flash('debug message', 'debug')
- >>> request.session.flash('info message', 'info')
- >>> messages = request.session.unflash()
- >>> info_messages = messages.debug()
- ['debug message']
- >>> info_messages = messages.info()
+ >>> request.session.flash('info message')
+ >>> request.session.peek_flash()
['info message']
-
-The full API of the ``messages`` object returned by ``unflash`` is documented
-in :class:`pyramid.interfaces.IFlashMessages`.
-
-.. The ``ignore_duplicate`` flag tells whether to suppress duplicate
-.. messages. If true, and another message with identical text exists in the
-.. queue, don't add the new message. But if the existing message has a
-.. different category than the new message, change its category to match the
-.. new message.
-
+ >>> request.session.peek_flash()
+ ['info message']
+ >>> request.session.pop_flash()
+ ['info message']
+ >>> request.session.peek_flash()
+ []
diff --git a/pyramid/flash.py b/pyramid/flash.py
deleted file mode 100644
index 9e9f44634..000000000
--- a/pyramid/flash.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from zope.interface import implements
-
-from pyramid.interfaces import IFlashMessages
-
-# flash message categories
-DEBUG = 'debug' # development messages
-INFO = 'info' # informational messages
-SUCCESS = 'success' # a message indicating success
-WARNING = 'warning' # not an error, but not a success
-ERROR = 'error' # an action was unsuccessful
-
-class FlashMessages(dict):
- implements(IFlashMessages)
- def custom(self, name):
- messages = self.get(name, [])
- return messages
-
- def debug(self):
- return self.get(DEBUG, [])
-
- def info(self):
- return self.get(INFO, [])
-
- def success(self):
- return self.get(SUCCESS, [])
-
- def warning(self):
- return self.get(WARNING, [])
-
- def error(self):
- return self.get(ERROR, [])
-
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 3ab833be8..45a63dc8b 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -120,34 +120,6 @@ class ITemplateRenderer(IRenderer):
accepts arbitrary keyword arguments and returns a string or
unicode object """
-class IFlashMessages(Interface):
- """ Dictionary-like object which maps flash category names to lists of
- flash messages. Also supports an API for obtaining classes of flash
- message lists."""
- def custom(name):
- """ Return a sequence of custom-category flash messages or an empty
- list if no messages of this custom category existed in the queue."""
-
- def debug():
- """ Return a sequence of flash.DEBUG category flash messages or an
- empty list if no flash.DEBUG messages existed in the queue."""
-
- def info():
- """ Return a sequence of flash.INFO category flash messages or an
- empty list if no flash.INFO messages existed in the queue."""
-
- def success():
- """ Return a sequence of flash.SUCCESS category flash messages or an
- empty list if no flash.SUCCESS messages existed in the queue."""
-
- def warning():
- """ Return a sequence of flash.WARNING category flash messages or an
- empty list if no flash.WARNING messages existed in the queue."""
-
- def error():
- """ Return a sequence of flash.ERROR category flash messages or an
- empty list if no flash.ERROR messages existed in the queue."""
-
# internal interfaces
class IRequest(Interface):
@@ -488,17 +460,24 @@ class ISession(Interface):
the sessioning machinery to notice the mutation of the
internal dictionary."""
- def flash(msg, category='info', queue_name=''):
- """ Push a flash message onto the stack related to the category and
- queue name. Multiple flash message queues can be managed by passing
- an optional ``queue_name``. Default category names are 'debug',
- 'info', 'success', 'warning', and 'error' (these have constant names
- importable from the ``pyramid.flash`` module). A custom category
- name is also permitted."""
-
- def unflash(queue_name=''):
- """ Pop a queue from the flash message storage. This method returns
- an object which implements ``pyramid.interfaces.IFlashMessages``"""
+ def flash(msg, queue='', allow_duplicate=True):
+ """ Push a flash message onto the end of the flash queue represented
+ by ``queue``. An alternate flash message queue can used by passing
+ an optional ``queue``, which must be a string. If
+ ``allow_duplicate`` is false, if the ``msg`` already exists in the
+ queue, it will not be readded."""
+
+ def pop_flash(queue=''):
+ """ Pop a queue from the flash storage. The queue is removed from
+ flash storage after this message is called. The queue is returned;
+ it is a list of flash messages added by
+ :meth:`pyramid.interfaces.ISesssion.flash`"""
+
+ def peek_flash(queue=''):
+ """ Peek at a queue in the flash storage. The queue remains in
+ flash storage after this message is called. The queue is returned;
+ it is a list of flash messages added by
+ :meth:`pyramid.interfaces.ISesssion.flash`"""
# mapping methods
diff --git a/pyramid/session.py b/pyramid/session.py
index 8c3bd1897..97db09e98 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -18,7 +18,6 @@ import base64
from zope.interface import implements
from pyramid.interfaces import ISession
-from pyramid import flash
def manage_accessed(wrapped):
""" Decorator which causes a cookie to be set when a wrapped
@@ -170,15 +169,19 @@ def UnencryptedCookieSessionFactoryConfig(
# flash API methods
@manage_accessed
- def flash(self, msg, category=flash.INFO, queue_name=''):
- storage = self.setdefault('_f_' + queue_name, {})
- category = storage.setdefault(category, [])
- category.append(msg)
+ def flash(self, msg, queue='', allow_duplicate=True):
+ storage = self.setdefault('_f_' + queue, [])
+ if allow_duplicate or (msg not in storage):
+ storage.append(msg)
@manage_accessed
- def unflash(self, queue_name=''):
- storage = self.pop('_f_' + queue_name, {})
- return flash.FlashMessages(storage)
+ def pop_flash(self, queue=''):
+ storage = self.pop('_f_' + queue, [])
+ return storage
+
+ def peek_flash(self, queue=''):
+ storage = self.get('_f_' + queue, [])
+ return storage
# non-API methods
def _set_cookie(self, response):
diff --git a/pyramid/tests/test_flash.py b/pyramid/tests/test_flash.py
deleted file mode 100644
index cce01d45b..000000000
--- a/pyramid/tests/test_flash.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import unittest
-
-class TestFlashMessages(unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.flash import FlashMessages
- return FlashMessages
-
- def _makeOne(self, *arg, **kw):
- cls = self._getTargetClass()
- return cls(*arg, **kw)
-
- def test_class_conforms(self):
- from zope.interface.verify import verifyClass
- from pyramid.interfaces import IFlashMessages
- verifyClass(IFlashMessages, self._getTargetClass())
-
- def test_instance_conforms(self):
- from zope.interface.verify import verifyObject
- from pyramid.interfaces import IFlashMessages
- messages = self._makeOne()
- verifyObject(IFlashMessages, messages)
-
- def test_debug_filled(self):
- from pyramid import flash
- expected = ['one', 'two']
- messages = self._makeOne({flash.DEBUG:expected})
- self.assertEqual(messages.debug(), expected)
-
- def test_debug_empty(self):
- messages = self._makeOne()
- self.assertEqual(messages.debug(), [])
-
- def test_info_filled(self):
- from pyramid import flash
- expected = ['one', 'two']
- messages = self._makeOne({flash.INFO:expected})
- self.assertEqual(messages.info(), expected)
-
- def test_info_empty(self):
- messages = self._makeOne()
- self.assertEqual(messages.info(), [])
-
- def test_success_filled(self):
- from pyramid import flash
- expected = ['one', 'two']
- messages = self._makeOne({flash.SUCCESS:expected})
- self.assertEqual(messages.success(), expected)
-
- def test_success_empty(self):
- messages = self._makeOne()
- self.assertEqual(messages.success(), [])
-
- def test_warning_filled(self):
- from pyramid import flash
- expected = ['one', 'two']
- messages = self._makeOne({flash.WARNING:expected})
- self.assertEqual(messages.warning(), expected)
-
- def test_warning_empty(self):
- messages = self._makeOne()
- self.assertEqual(messages.warning(), [])
-
- def test_error_filled(self):
- from pyramid import flash
- expected = ['one', 'two']
- messages = self._makeOne({flash.ERROR:expected})
- self.assertEqual(messages.error(), expected)
-
- def test_error_empty(self):
- messages = self._makeOne()
- self.assertEqual(messages.error(), [])
-
- def test_custom_filled(self):
- expected = ['one', 'two']
- messages = self._makeOne({'custom':expected})
- self.assertEqual(messages.custom('custom'), expected)
-
- def test_custom_empty(self):
- messages = self._makeOne()
- self.assertEqual(messages.custom('custom'), [])
-
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index bfaa6cd97..930e4697b 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -117,55 +117,52 @@ class TestUnencryptedCookieSession(unittest.TestCase):
session = self._makeOne(request)
session.flash('msg1')
session.flash('msg2')
- self.assertEqual(dict(session['_f_']),
- {'info':['msg1', 'msg2']})
+ self.assertEqual(session['_f_'], ['msg1', 'msg2'])
def test_flash_mixed(self):
- from pyramid import flash
request = testing.DummyRequest()
session = self._makeOne(request)
- session.flash('warn1', flash.WARNING)
- session.flash('warn2', flash.WARNING)
- session.flash('err1', flash.ERROR)
- session.flash('err2', flash.ERROR)
- self.assertEqual(dict(session['_f_']),
- {flash.WARNING:['warn1', 'warn2'],
- flash.ERROR:['err1', 'err2']})
+ session.flash('warn1', 'warn')
+ session.flash('warn2', 'warn')
+ session.flash('err1', 'error')
+ session.flash('err2', 'error')
+ self.assertEqual(session['_f_warn'], ['warn1', 'warn2'])
- def test_flash_with_nondefault_queue(self):
- from pyramid import flash
+ def test_pop_flash_default_queue(self):
request = testing.DummyRequest()
session = self._makeOne(request)
- session.flash('one_1', queue_name='one')
- session.flash('one_2', queue_name='one')
- session.flash('two_1', queue_name='two')
- session.flash('two_2', queue_name='two')
- self.assertEqual(dict(session['_f_one']),
- {flash.INFO:['one_1', 'one_2']})
- self.assertEqual(dict(session['_f_two']),
- {flash.INFO:['two_1', 'two_2']})
+ queue = ['one', 'two']
+ session['_f_'] = queue
+ result = session.pop_flash()
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_'), None)
- def test_unflash_default_queue(self):
- from pyramid import flash
- from pyramid.interfaces import IFlashMessages
+ def test_pop_flash_nodefault_queue(self):
request = testing.DummyRequest()
session = self._makeOne(request)
- storage = {flash.INFO:['one', 'two']}
- session['_f_'] = storage
- result = session.unflash()
- self.assertEqual(dict(result), storage)
- self.failUnless(IFlashMessages.providedBy(result))
+ queue = ['one', 'two']
+ session['_f_error'] = queue
+ result = session.pop_flash('error')
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_error'), None)
- def test_unflash_nodefault_queue(self):
- from pyramid import flash
- from pyramid.interfaces import IFlashMessages
+ def test_peek_flash_default_queue(self):
request = testing.DummyRequest()
session = self._makeOne(request)
- storage = {flash.INFO:['one', 'two']}
- session['_f_one'] = storage
- result = session.unflash('one')
- self.assertEqual(dict(result), storage)
- self.failUnless(IFlashMessages.providedBy(result))
+ queue = ['one', 'two']
+ session['_f_'] = queue
+ result = session.peek_flash()
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_'), queue)
+
+ def test_peek_flash_nodefault_queue(self):
+ request = testing.DummyRequest()
+ session = self._makeOne(request)
+ queue = ['one', 'two']
+ session['_f_error'] = queue
+ result = session.peek_flash('error')
+ self.assertEqual(result, queue)
+ self.assertEqual(session.get('_f_error'), queue)
class Test_manage_accessed(unittest.TestCase):
def _makeOne(self, wrapped):