------------------------------------------------------------
revno: 38
committer: Florian Fuchs <[email protected]>
branch nick: mailman.client
timestamp: Fri 2013-03-15 16:02:04 -0700
message:
  * Added list requests.
  * Held messages no longer mocked; tested via LMTP instead.
  * Removed usage of mocker library, added mock instead.
  * Added bin/test file for... testing
added:
  bin/
  bin/test
modified:
  setup.py
  src/mailmanclient/_client.py
  src/mailmanclient/docs/using.txt
  src/mailmanclient/tests/test_docs.py


--
lp:mailman.client
https://code.launchpad.net/~mailman-coders/mailman.client/trunk

Your team Mailman Coders is subscribed to branch lp:mailman.client.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman.client/trunk/+edit-subscription
=== added directory 'bin'
=== added file 'bin/test'
--- bin/test	1970-01-01 00:00:00 +0000
+++ bin/test	2013-03-15 23:02:04 +0000
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright (C) 2010-2013 by the Free Software Foundation, Inc.
+#
+# This file is part of mailman.client.
+#
+# mailman.client is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, version 3 of the License.
+#
+# mailman.client is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with mailman.client.  If not, see <http://www.gnu.org/licenses/>.
+
+"""Test runner."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+
+
+import os
+import sys
+import unittest
+
+from mailmanclient.tests.test_docs import additional_tests
+
+
+if __name__ == '__main__':
+    if len(sys.argv) == 1:
+        print 'Please provide the path to your mailman bin directory.'
+        sys.exit(1)
+    os.environ['MAILMAN_TEST_BINDIR'] = sys.argv[1]
+    suite = additional_tests()
+    runner = unittest.TextTestRunner(verbosity=2)
+    runner.run(suite)

=== modified file 'setup.py'
--- setup.py	2012-09-26 21:06:31 +0000
+++ setup.py	2013-03-15 23:02:04 +0000
@@ -45,8 +45,5 @@
     # Auto-conversion to Python 3.
     use_2to3=True,
     convert_2to3_doctests=find_doctests(),
-    install_requires = [
-        'httplib2',
-        'mocker',
-        ],
-    )
+    install_requires=['httplib2'],
+)

=== modified file 'src/mailmanclient/_client.py'
--- src/mailmanclient/_client.py	2012-11-02 14:46:38 +0000
+++ src/mailmanclient/_client.py	2013-03-15 23:02:04 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2010-2013 by the Free Software Foundation, Inc.
 #
 # This file is part of mailman.client.
 #
@@ -22,7 +22,7 @@
 __all__ = [
     'Client',
     'MailmanConnectionError',
-    ]
+]
 
 
 import re
@@ -39,7 +39,6 @@
 from mailmanclient import __version__
 
 
-
 def _member_key(member_dict):
     """Return the keys for sorting a member.
 
@@ -98,7 +97,7 @@
         """
         headers = {
             'User-Agent': 'GNU Mailman REST client v{0}'.format(__version__),
-            }
+        }
         if data is not None:
             data = urlencode(data, doseq=True)
             headers['Content-Type'] = 'application/x-www-form-urlencoded'
@@ -113,8 +112,8 @@
         url = urljoin(self.baseurl, path)
         try:
             response, content = Http().request(url, method, data, headers)
-            # If we did not get a 2xx status code, make this look like a urllib2
-            # exception, for backward compatibility.
+            # If we did not get a 2xx status code, make this look like a
+            # urllib2 exception, for backward compatibility.
             if response.status // 100 != 2:
                 raise HTTPError(url, response.status, content, response, None)
             if len(content) == 0:
@@ -160,8 +159,8 @@
         if 'entries' not in content:
             return []
         return [_List(self._connection, entry['self_link'])
-                for entry in sorted (content['entries'],
-                                     key=itemgetter('fqdn_listname'))]
+                for entry in sorted(content['entries'],
+                                    key=itemgetter('fqdn_listname'))]
 
     @property
     def domains(self):
@@ -242,7 +241,7 @@
     def get_list(self, fqdn_listname):
         response, content = self._connection.call(
             'lists/{0}'.format(fqdn_listname))
-        return _List(self._connection, content['self_link'])
+        return _List(self._connection, content['self_link'], content)
 
     def delete_list(self, fqdn_listname):
         response, content = self._connection.call(
@@ -297,8 +296,8 @@
         if 'entries' not in content:
             return []
         return [_List(self._connection, entry['self_link'])
-                for entry in sorted (content['entries'],
-                                     key=itemgetter('fqdn_listname'))]
+                for entry in sorted(content['entries'],
+                                    key=itemgetter('fqdn_listname'))]
 
     def create_list(self, list_name):
         fqdn_listname = '{0}@{1}'.format(list_name, self.mail_host)
@@ -308,10 +307,12 @@
 
 
 class _List:
-    def __init__(self, connection, url):
+    def __init__(self, connection, url, data=None):
         self._connection = connection
         self._url = url
         self._info = None
+        if data is not None:
+            self._info = data
 
     def __repr__(self):
         return '<List "{0}">'.format(self.fqdn_listname)
@@ -329,7 +330,7 @@
             return []
         else:
             return [item['address'] for item in content['entries']]
-        
+
     @property
     def moderators(self):
         url = self._url + '/roster/moderator'
@@ -377,7 +378,7 @@
     @property
     def settings(self):
         return _Settings(self._connection,
-            'lists/{0}/config'.format(self.fqdn_listname))
+                         'lists/{0}/config'.format(self.fqdn_listname))
 
     @property
     def held(self):
@@ -390,15 +391,37 @@
         else:
             entries = []
             for entry in content['entries']:
-                msg = dict(subject=entry['data']['_mod_subject'],
-                           fqdn_listname=entry['data']['_mod_fqdn_listname'],
-                           hold_date=entry['data']['_mod_hold_date'],
-                           reason=entry['data']['_mod_reason'],
-                           sender=entry['data']['_mod_sender'],
-                           id=entry['id'])
+                msg = dict(hold_date=entry['hold_date'],
+                           msg=entry['msg'],
+                           reason=entry['reason'],
+                           sender=entry['sender'],
+                           request_id=entry['request_id'],
+                           subject=entry['subject'])
                 entries.append(msg)
         return entries
 
+    @property
+    def requests(self):
+        """Return a list of dicts with subscription requests.
+        """
+        response, content = self._connection.call(
+            'lists/{0}/requests'.format(self.fqdn_listname), None, 'GET')
+        if 'entries' not in content:
+            return []
+        else:
+            entries = []
+            for entry in content['entries']:
+                request = dict(address=entry['address'],
+                               delivery_mode=entry['delivery_mode'],
+                               display_name=entry['display_name'],
+                               language=entry['language'],
+                               password=entry['password'],
+                               request_id=entry['request_id'],
+                               request_date=entry['when'],
+                               type=entry['type'])
+                entries.append(request)
+        return entries
+
     def add_owner(self, address):
         self.add_role('owner', address)
 
@@ -420,17 +443,17 @@
     def remove_role(self, role, address):
         url = 'lists/%s/%s/%s' % (self.fqdn_listname, role, address)
         self._connection.call(url, method='DELETE')
-        
+
     def moderate_message(self, request_id, action):
         """Moderate a held message.
-        
+
         :param request_id: Id of the held message.
         :type request_id: Int.
         :param action: Action to perform on held message.
         :type action: String.
         """
-        path = 'lists/{0}/held/{1}'.format(self.fqdn_listname, 
-                                          str(request_id))
+        path = 'lists/{0}/held/{1}'.format(self.fqdn_listname,
+                                           str(request_id))
         response, content = self._connection.call(path, dict(action=action),
                                                   'POST')
         return response
@@ -484,7 +507,7 @@
             list_id=self.list_id,
             subscriber=address,
             display_name=display_name,
-            )
+        )
         response, content = self._connection.call('members', data)
         return _Member(self._connection, response['location'])
 
@@ -504,7 +527,6 @@
             raise ValueError('%s is not a member address of %s' %
                              (address, self.fqdn_listname))
 
-
     def delete(self):
         response, content = self._connection.call(
             'lists/{0}'.format(self.fqdn_listname), None, 'DELETE')
@@ -524,7 +546,7 @@
         if self._info is None:
             response, content = self._connection.call(self._url)
             self._info = content
-        
+
     @property
     def list_id(self):
         self._get_info()
@@ -534,7 +556,7 @@
     def role(self):
         self._get_info()
         return self._info['role']
-        
+
     @property
     def address(self):
         self._get_info()
@@ -584,7 +606,7 @@
     @property
     def addresses(self):
         return _Addresses(self._connection, self.user_id)
-    
+
     @property
     def display_name(self):
         self._get_info()
@@ -633,7 +655,7 @@
 
     def delete(self):
         response, content = self._connection.call(self._url, method='DELETE')
-        
+
 
 class _Addresses:
     def __init__(self, connection, user_id):
@@ -654,6 +676,7 @@
         for address in self._addresses:
             yield _Address(self._connection, address)
 
+
 class _Preferences:
     def __init__(self, connection, address):
         self._connection = connection
@@ -663,15 +686,17 @@
 
     def _get_preferences(self):
         if self._preferences is None:
-            response, content = self._connection.call('addresses/{0}/preferences'.format(self._address))
+            response, content = self._connection.call(
+                'addresses/{0}/preferences'.format(self._address))
             if 'entries' not in content:
-                self._preferences= []
+                self._preferences = []
             self._preferences = content['entries']
 
     def __iter__(self):
         for preference in self._preferences:
             yield _Preference(self._connection, preference)
 
+
 class _Address:
     def __init__(self, connection, address):
         self._connection = connection

=== modified file 'src/mailmanclient/docs/using.txt'
--- src/mailmanclient/docs/using.txt	2012-09-26 21:06:31 +0000
+++ src/mailmanclient/docs/using.txt	2013-03-15 23:02:04 +0000
@@ -4,10 +4,10 @@
 
     >>> import os
     >>> import time
+    >>> import smtplib
     >>> import subprocess
 
-    >>> from mocker import Mocker
-    >>> mocker = Mocker()
+    >>> from mock import patch
 
 This is the official Python bindings for the GNU Mailman REST API.  In order
 to talk to Mailman, the engine's REST server must be running.  You begin by
@@ -64,8 +64,8 @@
     <Domain "example.com">
     >>> print example_dot_com.base_url
     http://example.com
-    
-Additionally you can get an existing domain using its web host. 
+
+Additionally you can get an existing domain using its web host.
 
     >>> example = client.get_domain(web_host='http://example.com')
     >>> example
@@ -91,7 +91,7 @@
     <Domain "example.com">
     <Domain "example.net">
     <Domain "example.org">
-    
+
 Also, domain can be deleted.
 
     >>> client.delete_domain('example.org')
@@ -141,7 +141,7 @@
     <List "[email protected]">
     <List "[email protected]">
     <List "[email protected]">
-    
+
 If you only want to know all lists for a specific domain, use the domain object.
 
     >>> for mlist in example.lists:
@@ -272,7 +272,7 @@
     >>> print cris.display_name
     Cris
 
-Every user has a list of one or more addresses. 
+Every user has a list of one or more addresses.
 
     >>> for address in cris.addresses:
     ...     print address
@@ -346,7 +346,7 @@
 
     >>> settings = test_one.settings
     >>> len(settings)
-    47
+    48
 
     >>> for attr in sorted(settings):
     ...     print attr + ': ' + str(settings[attr])
@@ -445,7 +445,7 @@
     <Member "[email protected]" on "test-one.example.com">
 
     >>> for member in client.members:
-    ...     print '%s: %s' %(member, member.role) 
+    ...     print '%s: %s' %(member, member.role)
     <Member "[email protected]" on "test-one.example.com">: moderator
     <Member "[email protected]" on "test-one.example.com">: member
     <Member "[email protected]" on "test-one.example.com">: member
@@ -466,24 +466,23 @@
 Moderation
 ==========
 
-    >>> from mailmanclient._client import _Connection
-    >>> connection = mocker.patch(_Connection)
-    >>> connection.call('lists/[email protected]/held', None, 'GET')
-    <mocker.Mock object at ...>
-    >>> mocker.result([200, {'entries': [
-    ...     {u'data': {
-    ...         u'_mod_subject': u'Something',
-    ...         u'_mod_fqdn_listname': u'[email protected]',
-    ...         u'_mod_hold_date': u'2005-08-01T07:49:23',
-    ...         u'_mod_reason': u'Because',
-    ...         u'_mod_sender': u'[email protected]',
-    ...         },
-    ...      u'id': 123,},
-    ...     ]}])
-    >>> connection.call('lists/[email protected]/held/123', dict(action=u'defer'), 'POST')
-    <mocker.Mock object at ...>
-    >>> mocker.result([204, {u'status': 204}])
-    >>> mocker.replay()
+Message Moderation
+------------------
+
+    >>> msg = """From: [email protected]
+    ... To: [email protected]
+    ... Subject: Something
+    ... Message-ID: <moderated_01>
+    ... 
+    ... Some text.
+    ... 
+    ... """
+    >>> server = smtplib.LMTP('localhost', 8024)
+    >>> server.sendmail('[email protected]', '[email protected]', msg)
+    {}
+    >>> server.quit()
+    (221, 'Bye')
+    >>> time.sleep(2)
 
 Messages held for moderation can be listed on a per list basis.
 
@@ -491,12 +490,57 @@
     >>> print held[0]['subject']
     Something
     >>> print held[0]['reason']
-    Because
-    >>> print held[0]['id']
-    123
-
-    >>> result = test_one.defer_message(held[0]['id'])
-    >>> print result
-    204
-
-    >>> mocker.restore()
+    <BLANKLINE>
+    >>> print held[0]['request_id']
+    1
+
+    >>> print test_one.defer_message(held[0]['request_id'])['status']
+    204
+
+    >>> len(test_one.held)
+    1
+
+    >>> print test_one.discard_message(held[0]['request_id'])['status']
+    204
+
+    >>> len(test_one.held)
+    0
+
+
+Subscription Moderation
+-----------------------
+
+    >>> patcher = patch('mailmanclient._client._Connection.call')
+    >>> mock_call = patcher.start()
+    >>> mock_call.return_value = [None, dict(entries=[
+    ...     dict(address=u'[email protected]',
+    ...          delivery_mode=u'regular',
+    ...          display_name=u'Ler',
+    ...          http_etag=u'...',
+    ...          language=u'en',
+    ...          password=u'password',
+    ...          request_id=1,
+    ...          type=u'subscription',
+    ...          when=u'2005-08-01T07:49:23',
+    ...     )
+    ... ])]
+
+    >>> requests = test_one.requests
+    >>> print requests[0]['address']
+    [email protected]
+    >>> print requests[0]['delivery_mode']
+    regular
+    >>> print requests[0]['display_name']
+    Ler
+    >>> print requests[0]['language']
+    en
+    >>> print requests[0]['password']
+    password
+    >>> print requests[0]['request_id']
+    1
+    >>> print requests[0]['type']
+    subscription
+    >>> print requests[0]['request_date']
+    2005-08-01T07:49:23
+
+    >>> patcher.stop()

=== modified file 'src/mailmanclient/tests/test_docs.py'
--- src/mailmanclient/tests/test_docs.py	2012-09-26 21:06:31 +0000
+++ src/mailmanclient/tests/test_docs.py	2013-03-15 23:02:04 +0000
@@ -96,21 +96,21 @@
 [runner.bounces]
 start: no
 [runner.command]
-start: no
+start: yes
 [runner.in]
-start: no
+start: yes
 [runner.lmtp]
-start: no
+start: yes
 [runner.news]
 start: no
 [runner.out]
-start: no
+start: yes
 [runner.pipeline]
 start: no
 [runner.retry]
 start: no
 [runner.virgin]
-start: no
+start: yes
 [runner.digest]
 start: no
 """.format(vardir=vardir)

_______________________________________________
Mailman-coders mailing list
[email protected]
http://mail.python.org/mailman/listinfo/mailman-coders

Reply via email to