------------------------------------------------------------
revno: 6742
committer: Barry Warsaw <[email protected]>
branch nick: 3.0
timestamp: Tue 2009-06-30 23:14:26 -0400
message:
  Many pylint cleanups.
modified:
  pylint.rc
  src/mailman/Utils.py
  src/mailman/app/bounces.py
  src/mailman/app/commands.py
  src/mailman/app/finder.py
  src/mailman/app/lifecycle.py
  src/mailman/app/membership.py
  src/mailman/constants.py
  src/mailman/domain.py
  src/mailman/i18n.py
  src/mailman/inject.py
  src/mailman/interact.py
  src/mailman/options.py
  src/mailman/passwords.py
  src/mailman/version.py

=== modified file 'pylint.rc'
--- pylint.rc   2009-06-30 10:14:49 +0000
+++ pylint.rc   2009-07-01 03:14:26 +0000
@@ -53,8 +53,14 @@
 #enable-msg=
 
 # Disable the message(s) with the given id(s).
+# C0103: *Invalid name %s* in module globals
 # I0011: *Locally disabling %s*
-disable-msg=I0011
+# R0903: *Too few public methods*
+# R0913: *Too many arguments*
+# R0914: *Too many local variables*
+# W0142: *Used * or ** magic*
+# W0704: *Except doesn't do anything*
+disable-msg=C0103,I0011,R0903,R0914,W0142,W0704
 
 
 [REPORTS]
@@ -250,7 +256,7 @@
 [IMPORTS]
 
 # Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+deprecated-modules=regsub,TERMIOS,Bastion,rexec
 
 # Create a graph of every (i.e. internal and external) dependencies in the
 # given file (report R0402 must not be disabled)

=== modified file 'src/mailman/Utils.py'
--- src/mailman/Utils.py        2009-02-13 03:55:01 +0000
+++ src/mailman/Utils.py        2009-07-01 03:14:26 +0000
@@ -39,6 +39,7 @@
 import logging
 import htmlentitydefs
 
+# pylint: disable-msg=E0611,W0403
 from email.errors import HeaderParseError
 from email.header import decode_header, make_header
 from lazr.config import as_boolean
@@ -71,7 +72,8 @@
 
 
 
-# a much more naive implementation than say, Emacs's fill-paragraph!
+# A much more naive implementation than say, Emacs's fill-paragraph!
+# pylint: disable-msg=R0912
 def wrap(text, column=70, honor_leading_ws=True):
     """Wrap and fill the text to the specified column.
 
@@ -179,7 +181,7 @@
                     try:
                         fd = os.open('/dev/urandom', os.O_RDONLY)
                     except OSError, e:
-                        if e.errno <> errno.ENOENT:
+                        if e.errno != errno.ENOENT:
                             raise
                         # We have no available source of cryptographically
                         # secure random characters.  Log an error and fallback
@@ -246,7 +248,7 @@
         challenge = fp.read()[:-1]                # strip off trailing nl
         fp.close()
     except IOError, e:
-        if e.errno <> errno.ENOENT:
+        if e.errno != errno.ENOENT:
             raise
         # It's okay not to have a site admin password
         return None
@@ -378,7 +380,7 @@
                     fp = open(filename)
                     raise OuterExit
                 except IOError, e:
-                    if e.errno <> errno.ENOENT:
+                    if e.errno != errno.ENOENT:
                         raise
                     # Okay, it doesn't exist, keep looping
                     fp = None
@@ -391,7 +393,7 @@
             filename = os.path.join(TEMPLATE_DIR, 'en', templatefile)
             fp = open(filename)
         except IOError, e:
-            if e.errno <> errno.ENOENT:
+            if e.errno != errno.ENOENT:
                 raise
             # We never found the template.  BAD!
             raise IOError(errno.ENOENT, 'No template file found', templatefile)

=== modified file 'src/mailman/app/bounces.py'
--- src/mailman/app/bounces.py  2009-02-13 01:36:21 +0000
+++ src/mailman/app/bounces.py  2009-07-01 03:14:26 +0000
@@ -30,7 +30,7 @@
 from email.mime.text import MIMEText
 
 from mailman.Utils import oneline
-from mailman.email.message import Message, UserNotification
+from mailman.email.message import UserNotification
 from mailman.i18n import _
 
 log = logging.getLogger('mailman.config')
@@ -38,6 +38,15 @@
 
 
 def bounce_message(mlist, msg, e=None):
+    """Bounce the message back to the original author.
+
+    :param mlist: The mailing list that the message was posted to.
+    :type mlist: `IMailingList`
+    :param msg: The original message.
+    :type msg: `email.message.Message`
+    :param e: Optional exception causing the bounce.
+    :type e: Exception
+    """
     # Bounce a message back to the sender, with an error message if provided
     # in the exception argument.
     if msg.sender is None:

=== modified file 'src/mailman/app/commands.py'
--- src/mailman/app/commands.py 2009-03-29 20:40:22 +0000
+++ src/mailman/app/commands.py 2009-07-01 03:14:26 +0000
@@ -40,5 +40,5 @@
         verifyObject(IEmailCommand, command)
         assert command_class.name not in config.commands, (
             'Duplicate email command "{0}" found in {1}'.format(
-                command_class.name, module))
+                command_class.name, command_class.__module__))
         config.commands[command_class.name] = command_class()

=== modified file 'src/mailman/app/finder.py'
--- src/mailman/app/finder.py   2009-03-29 20:40:22 +0000
+++ src/mailman/app/finder.py   2009-07-01 03:14:26 +0000
@@ -27,6 +27,7 @@
 
 import os
 import sys
+
 from pkg_resources import resource_listdir
 
 
@@ -45,7 +46,7 @@
     # Find all rules found in all modules inside our package.
     for filename in resource_listdir(package, ''):
         basename, extension = os.path.splitext(filename)
-        if extension <> '.py':
+        if extension != '.py':
             continue
         module_name = '{0}.{1}'.format(package, basename)
         __import__(module_name, fromlist='*')

=== modified file 'src/mailman/app/lifecycle.py'
--- src/mailman/app/lifecycle.py        2009-05-27 21:47:21 +0000
+++ src/mailman/app/lifecycle.py        2009-07-01 03:14:26 +0000
@@ -17,7 +17,7 @@
 
 """Application level list creation."""
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
@@ -27,11 +27,9 @@
 
 
 import os
-import sys
 import shutil
 import logging
 
-from mailman import Utils
 from mailman.config import config
 from mailman.core import errors
 from mailman.email.validate import validate
@@ -48,6 +46,7 @@
     if owners is None:
         owners = []
     validate(fqdn_listname)
+    # pylint: disable-msg=W0612
     listname, domain = fqdn_listname.split('@', 1)
     if domain not in config.domains:
         raise errors.BadDomainSpecificationError(domain)

=== modified file 'src/mailman/app/membership.py'
--- src/mailman/app/membership.py       2009-02-13 03:52:18 +0000
+++ src/mailman/app/membership.py       2009-07-01 03:14:26 +0000
@@ -17,7 +17,7 @@
 
 """Application support for membership management."""
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
@@ -33,7 +33,7 @@
 from mailman.app.notifications import send_goodbye_message
 from mailman.config import config
 from mailman.core import errors
-from mailman.email.message import Message, OwnerNotification
+from mailman.email.message import OwnerNotification
 from mailman.email.validate import validate
 from mailman.interfaces.member import AlreadySubscribedError, MemberRole
 
@@ -47,17 +47,17 @@
     The member's subscription must be approved by whatever policy the list
     enforces.
 
-    :param mlist: the mailing list to add the member to
-    :type mlist: IMailingList
-    :param address: the address to subscribe
+    :param mlist: The mailing list to add the member to.
+    :type mlist: `IMailingList`
+    :param address: The address to subscribe.
     :type address: string
-    :param realname: the subscriber's full name
+    :param realname: The subscriber's full name.
     :type realname: string
-    :param password: the subscriber's password
+    :param password: The subscriber's password.
     :type password: string
-    :param delivery_mode: the delivery mode the subscriber has chosen
+    :param delivery_mode: The delivery mode the subscriber has chosen.
     :type delivery_mode: DeliveryMode
-    :param language: the language that the subscriber is going to use
+    :param language: The language that the subscriber is going to use.
     :type language: string
     """
     # Let's be extra cautious.
@@ -104,6 +104,7 @@
             raise AssertionError(
                 'User should have had linked address: {0}'.format(address))
         # Create the member and set the appropriate preferences.
+        # pylint: disable-msg=W0631
         member = address_obj.subscribe(mlist, MemberRole.member)
         member.preferences.preferred_language = language
         member.preferences.delivery_mode = delivery_mode
@@ -113,6 +114,17 @@
 
 
 def delete_member(mlist, address, admin_notif=None, userack=None):
+    """Delete a member right now.
+
+    :param mlist: The mailing list to add the member to.
+    :type mlist: `IMailingList`
+    :param address: The address to subscribe.
+    :type address: string
+    :param admin_notif: Whether the list administrator should be notified that
+        this member was deleted.
+    :type admin_notif: bool, or None to let the mailing list's
+        `admin_notify_mchange` attribute decide.
+    """
     if userack is None:
         userack = mlist.send_goodbye_msg
     if admin_notif is None:

=== modified file 'src/mailman/constants.py'
--- src/mailman/constants.py    2009-02-13 01:36:21 +0000
+++ src/mailman/constants.py    2009-07-01 03:14:26 +0000
@@ -33,7 +33,13 @@
 
 
 
+# pylint: disable-msg=W0232
+# no class __init__()
+# pylint: disable-msg=R0903
+# too few public methods
 class SystemDefaultPreferences:
+    """The default system preferences."""
+
     implements(IPreferences)
 
     acknowledge_posts = False

=== modified file 'src/mailman/domain.py'
--- src/mailman/domain.py       2009-01-17 02:04:21 +0000
+++ src/mailman/domain.py       2009-07-01 03:14:26 +0000
@@ -59,6 +59,8 @@
         self.contact_address = (contact_address
                                 if contact_address is not None
                                 else 'postmaster@' + email_host)
+        # pylint: disable-msg=E1101
+        # no netloc member; yes it does
         self.url_host = urlparse(self.base_url).netloc
 
     def confirm_address(self, token=''):

=== modified file 'src/mailman/i18n.py'
--- src/mailman/i18n.py 2009-02-13 01:36:21 +0000
+++ src/mailman/i18n.py 2009-07-01 03:14:26 +0000
@@ -46,10 +46,12 @@
 
 
 class Template(string.Template):
+    """Match any attribute path."""
     idpattern = r'[_a-z][_a-z0-9.]*'
 
 
 class attrdict(dict):
+    """Follow attribute paths."""
     def __getitem__(self, key):
         parts = key.split('.')
         value = super(attrdict, self).__getitem__(parts.pop(0))
@@ -62,6 +64,12 @@
 
 
 def set_language(language_code=None):
+    """Set the global translation context from a language code.
+
+    :param language_code: The two letter language code to set.
+    :type language_code: str
+    """
+    # pylint: disable-msg=W0603
     global _translation
     # gettext.translation() API requires None or a sequence.
     codes = (None if language_code is None else [language_code])
@@ -74,10 +82,21 @@
 
 
 def get_translation():
+    """Return the global translation context.
+
+    :return: The global translation context.
+    :rtype: `GNUTranslation`
+    """
     return _translation
 
 
 def set_translation(translation):
+    """Set the global translation context.
+
+    :param translation: The translation context.
+    :type translation: `GNUTranslation`.
+    """
+    # pylint: disable-msg=W0603
     global _translation
     _translation = translation
 
@@ -92,7 +111,9 @@
         self._old_translation = _translation
         set_language(self._language_code)
 
+    # pylint: disable-msg=W0613
     def __exit__(self, *exc_info):
+        # pylint: disable-msg=W0603
         global _translation
         _translation = self._old_translation
         # Do not suppress exceptions.
@@ -107,6 +128,13 @@
 
 
 def _(s):
+    """Translate the string.
+
+    :param s: The string to transate
+    :type s: string
+    :return: The translated string
+    :rtype: string
+    """
     if s == '':
         return ''
     assert s, 'Cannot translate: {0}'.format(s)
@@ -122,6 +150,7 @@
     # original string is 1) locals dictionary, 2) globals dictionary.
     #
     # Get the frame of the caller.
+    # pylint: disable-msg=W0212
     frame = sys._getframe(1)
     # A `safe' dictionary is used so we won't get an exception if there's a
     # missing key in the dictionary.
@@ -144,6 +173,13 @@
 
 
 def ctime(date):
+    """Translate a ctime.
+
+    :param date: The date to translate.
+    :type date: str or time float
+    :return: The translated date.
+    :rtype: string
+    """
     # Don't make these module globals since we have to do runtime translation
     # of the strings anyway.
     daysofweek = [
@@ -156,6 +192,7 @@
         _('Jul'), _('Aug'), _('Sep'), _('Oct'), _('Nov'), _('Dec')
         ]
 
+    # pylint: disable-msg=W0612
     tzname = _('Server Local Time')
     if isinstance(date, str):
         try:
@@ -189,6 +226,7 @@
                         mon = i
                         break
     else:
+        # pylint: disable-msg=W0612
         year, mon, day, hh, mm, ss, wday, yday, dst = time.localtime(date)
         if dst in (0, 1):
             tzname = time.tzname[dst]

=== modified file 'src/mailman/inject.py'
--- src/mailman/inject.py       2009-02-20 03:23:42 +0000
+++ src/mailman/inject.py       2009-07-01 03:14:26 +0000
@@ -15,6 +15,8 @@
 # You should have received a copy of the GNU General Public License along with
 # GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
 
+"""Inject a message into a queue."""
+
 from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
@@ -24,6 +26,8 @@
     ]
 
 
+# pylint doesn't understand absolute_import
+# pylint: disable-msg=E0611,W0403
 from email import message_from_string
 from email.utils import formatdate, make_msgid
 

=== modified file 'src/mailman/interact.py'
--- src/mailman/interact.py     2009-01-17 02:04:21 +0000
+++ src/mailman/interact.py     2009-07-01 03:14:26 +0000
@@ -33,19 +33,31 @@
 
 
 def interact(upframe=True, banner=DEFAULT_BANNER, overrides=None):
-    # The interactive prompt's namespace
-    ns = dict()
-    # If uplocals is true, also populate the console's locals with the locals
-    # of the frame that called this function (i.e. one up from here).
+    """Start an interactive interpreter prompt.
+
+    :param upframe: Whether or not to populate the interpreter's globals with
+        the locals from the frame that called this function.
+    :type upfframe: bool
+    :param banner: The banner to print before the interpreter starts.
+    :type banner: string
+    :param overrides: Additional interpreter globals to add.
+    :type overrides: dict
+    """
+    # The interactive prompt's namespace.
+    namespace = dict()
+    # Populate the console's with the locals of the frame that called this
+    # function (i.e. one up from here).
     if upframe:
+        # pylint: disable-msg=W0212
         frame = sys._getframe(1)
-        ns.update(frame.f_globals)
-        ns.update(frame.f_locals)
+        namespace.update(frame.f_globals)
+        namespace.update(frame.f_locals)
     if overrides is not None:
-        ns.update(overrides)
-    interp = code.InteractiveConsole(ns)
-    # Try to import the readline module, but don't worry if it's unavailable
+        namespace.update(overrides)
+    interp = code.InteractiveConsole(namespace)
+    # Try to import the readline module, but don't worry if it's unavailable.
     try:
+        # pylint: disable-msg=W0612
         import readline
     except ImportError:
         pass
@@ -54,8 +66,9 @@
     # than once, this could cause a problem.
     startup = os.environ.get('PYTHONSTARTUP')
     if startup:
+        # pylint: disable-msg=W0702
         try:
-            execfile(startup, ns)
+            execfile(startup, namespace)
         except:
             pass
     # We don't want the funky console object in parentheses in the banner.

=== modified file 'src/mailman/options.py'
--- src/mailman/options.py      2009-05-15 22:40:41 +0000
+++ src/mailman/options.py      2009-07-01 03:14:26 +0000
@@ -17,7 +17,7 @@
 
 """Common argument parsing."""
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
@@ -40,7 +40,9 @@
 
 
 
+# pylint: disable-msg=W0613
 def check_unicode(option, opt, value):
+    """Check that the value is a unicode string."""
     if isinstance(value, unicode):
         return value
     try:
@@ -50,7 +52,9 @@
             'option {0}: Cannot decode: {1}'.format(opt, value))
 
 
+# pylint: disable-msg=W0613
 def check_yesno(option, opt, value):
+    """Check that the value is 'yes' or 'no'."""
     value = value.lower()
     if value not in ('yes', 'no', 'y', 'n'):
         raise OptionValueError('option {0}: invalid: {1}'.format(opt, value))
@@ -73,6 +77,7 @@
     having to wrap the options in str() calls.
     """
     def add_option(self, *args, **kwargs):
+        """See `OptionParser`."""
         # Check to see if the first or first two options are unicodes and turn
         # them into 8-bit strings before calling the superclass's method.
         if len(args) == 0:
@@ -150,6 +155,7 @@
     """A helper for specifying the mailing list on the command line."""
 
     def add_options(self):
+        """See `Options`."""
         self.parser.add_option(
             '-l', '--listname',
             type='unicode', help=_('The mailing list name'))
@@ -160,6 +166,7 @@
     """A helper for specifying multiple mailing lists on the command line."""
 
     def add_options(self):
+        """See `Options`."""
         self.parser.add_option(
             '-l', '--listname',
             default=[], action='append', dest='listnames', type='unicode',

=== modified file 'src/mailman/passwords.py'
--- src/mailman/passwords.py    2009-01-29 19:06:24 +0000
+++ src/mailman/passwords.py    2009-07-01 03:14:26 +0000
@@ -20,13 +20,13 @@
 Represents passwords using RFC 2307 syntax (as best we can tell).
 """
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
     'Schemes',
+    'check_response',
     'make_secret',
-    'check_response',
     ]
 
 
@@ -47,71 +47,102 @@
 
 
 
+# pylint: disable-msg=W0232
 class PasswordScheme:
+    """Password scheme base class."""
     TAG = b''
 
     @staticmethod
     def make_secret(password):
-        """Return the hashed password"""
+        """Return the hashed password.
+
+        :param password: The clear text password.
+        :type password: string
+        :return: The encrypted password.
+        :rtype: string
+        """
         raise NotImplementedError
 
     @staticmethod
     def check_response(challenge, response):
-        """Return True if response matches challenge.
+        """Check a response against a challenge.
 
         It is expected that the scheme specifier prefix is already stripped
         from the response string.
+
+        :param challenge: The challenge.
+        :type challenge: string
+        :param response: The response.
+        :type response: string
+        :return: True if the response matches the challenge.
+        :rtype: bool
         """
         raise NotImplementedError
 
 
 
 class NoPasswordScheme(PasswordScheme):
+    """A password scheme without passwords."""
+
     TAG = b'NONE'
 
     @staticmethod
     def make_secret(password):
+        """See `PasswordScheme`."""
         return b''
 
+    # pylint: disable-msg=W0613
     @staticmethod
     def check_response(challenge, response):
+        """See `PasswordScheme`."""
         return False
 
 
 
 class ClearTextPasswordScheme(PasswordScheme):
+    """A password scheme that stores clear text passwords."""
+
     TAG = b'CLEARTEXT'
 
     @staticmethod
     def make_secret(password):
+        """See `PasswordScheme`."""
         return password
 
     @staticmethod
     def check_response(challenge, response):
+        """See `PasswordScheme`."""
         return challenge == response
 
 
 
 class SHAPasswordScheme(PasswordScheme):
+    """A password scheme that encodes the password using SHA1."""
+
     TAG = b'SHA'
 
     @staticmethod
     def make_secret(password):
+        """See `PasswordScheme`."""
         h = hashlib.sha1(password)
         return encode(h.digest())
 
     @staticmethod
     def check_response(challenge, response):
+        """See `PasswordScheme`."""
         h = hashlib.sha1(response)
         return challenge == encode(h.digest())
 
 
 
 class SSHAPasswordScheme(PasswordScheme):
+    """A password scheme that encodes the password using salted SHA1."""
+
     TAG = b'SSHA'
 
     @staticmethod
     def make_secret(password):
+        """See `PasswordScheme`."""
         salt = os.urandom(SALT_LENGTH)
         h = hashlib.sha1(password)
         h.update(salt)
@@ -119,6 +150,7 @@
 
     @staticmethod
     def check_response(challenge, response):
+        """See `PasswordScheme`."""
         # Get the salt from the challenge
         challenge_bytes = decode(challenge)
         digest = challenge_bytes[:20]
@@ -131,6 +163,8 @@
 
 # Basic algorithm given by Bob Fleck
 class PBKDF2PasswordScheme(PasswordScheme):
+    """RFC 2989 password encoding scheme."""
+
     # This is a bit nasty if we wanted a different prf or iterations.  OTOH,
     # we really have no clue what the standard LDAP-ish specification for
     # those options is.
@@ -157,7 +191,9 @@
 
     @staticmethod
     def make_secret(password):
-        """From RFC2898 sec. 5.2.  Simplified to handle only 20 byte output
+        """See `PasswordScheme`.
+
+        From RFC2898 sec. 5.2.  Simplified to handle only 20 byte output
         case.  Output of 20 bytes means always exactly one block to handle,
         and a constant block counter appended to the salt in the initial hmac
         update.
@@ -167,11 +203,13 @@
         derived_key = encode(digest + salt)
         return derived_key
 
+    # pylint: disable-msg=W0221
     @staticmethod
     def check_response(challenge, response, prf, iterations):
-        # Decode the challenge to get the number of iterations and salt
-        # XXX we don't support anything but sha prf
-        if prf.lower() <> b'sha':
+        """See `PasswordScheme`."""
+        # Decode the challenge to get the number of iterations and salt.  We
+        # don't support anything but SHA PRF.
+        if prf.lower() != b'sha':
             return False
         try:
             iterations = int(iterations)
@@ -186,6 +224,7 @@
 
 
 class Schemes(Enum):
+    """List of password schemes."""
     # no_scheme is deliberately ugly because no one should be using it.  Yes,
     # this makes cleartext inconsistent, but that's a common enough
     # terminology to justify the missing underscore.
@@ -215,6 +254,15 @@
 
 
 def make_secret(password, scheme=None):
+    """Encrypt a password.
+
+    :param password: The clear text password.
+    :type password: string
+    :param scheme: The password scheme name.
+    :type scheme: string
+    :return: The encrypted password.
+    :rtype: string
+    """
     # The hash algorithms operate on bytes not strings.  The password argument
     # as provided here by the client will be a string (in Python 2 either
     # unicode or 8-bit, in Python 3 always unicode).  We need to encode this
@@ -231,6 +279,15 @@
 
 
 def check_response(challenge, response):
+    """Check a response against a challenge.
+
+    :param challenge: The challenge.
+    :type challenge: string
+    :param response: The response.
+    :type response: string
+    :return: True if the response matches the challenge.
+    :rtype: bool
+    """
     mo = re.match(r'{(?P<scheme>[^}]+?)}(?P<rest>.*)',
                   challenge, re.IGNORECASE)
     if not mo:
@@ -250,4 +307,11 @@
 
 
 def lookup_scheme(scheme_name):
+    """Look up a password scheme.
+
+    :param scheme_name: The password scheme name.
+    :type scheme_name: string
+    :return: Password scheme class.
+    :rtype: `PasswordScheme`
+    """
     return _SCHEMES_BY_TAG.get(scheme_name.lower())

=== modified file 'src/mailman/version.py'
--- src/mailman/version.py      2009-04-01 20:14:24 +0000
+++ src/mailman/version.py      2009-07-01 03:14:26 +0000
@@ -15,6 +15,8 @@
 # You should have received a copy of the GNU General Public License along with
 # GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
 
+"""Mailman version strings."""
+
 # Mailman version
 VERSION = "3.0.0a3"
 CODENAME = 'Working Man'



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

Your team Mailman Checkins is subscribed to branch lp:mailman.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+edit-subscription.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to