[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-06 Thread noreply

revno: 6545
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Mon 2007-08-06 10:53:01 -0500
message:
  Simple update.
modified:
  Mailman/bin/version.py

=== modified file 'Mailman/bin/version.py'
--- a/Mailman/bin/version.py2007-01-19 04:38:06 +
+++ b/Mailman/bin/version.py2007-08-06 15:53:01 +
@@ -32,9 +32,7 @@
 Print the Mailman version and exit.))
 opts, args = parser.parse_args()
 if args:
-parser.print_help()
-print  sys.stderr, _('Unexpected arguments')
-sys.exit(1)
+parser.error(_('Unexpected arguments'))
 return parser, opts, args
 
 
@@ -42,7 +40,7 @@
 def main():
 parser, opts, args = parseargs()
 # Yes, this is kind of silly
-print _('Using Mailman version: $Version.MAILMAN_VERSION')
+print _('Using $Version.MAILMAN_VERSION')
 
 
 



--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-06 Thread noreply

revno: 6544
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Mon 2007-08-06 10:50:27 -0500
message:
  Make bin/list_owners work again.
modified:
  Mailman/bin/list_owners.py

=== modified file 'Mailman/bin/list_owners.py'
--- a/Mailman/bin/list_owners.py2007-05-28 20:21:41 +
+++ b/Mailman/bin/list_owners.py2007-08-06 15:50:27 +
@@ -22,6 +22,8 @@
 from Mailman.MailList import MailList
 from Mailman.configuration import config
 from Mailman.i18n import _
+from Mailman.initialize import initialize
+
 
 __i18n_templates__ = True
 
@@ -52,16 +54,17 @@
 
 def main():
 parser, opts, args = parseargs()
-config.load(opts.config)
+initialize(opts.config)
 
-listnames = set(args or config.list_manager.names)
+listmgr = config.db.list_manager
+listnames = set(args or listmgr.names)
 bylist = {}
 
 for listname in listnames:
-mlist = MailList(listname, lock=False)
-addrs = mlist.owner[:]
+mlist = listmgr.get(listname)
+addrs = [addr.address for addr in mlist.owners.addresses]
 if opts.moderators:
-addrs.extend(mlist.moderator)
+addrs.extend([addr.address for addr in mlist.moderators.addresses])
 bylist[listname] = addrs
 
 if opts.with_listnames:
@@ -79,9 +82,7 @@
 for listname in listnames:
 for addr in bylist[listname]:
 unique.add(addr)
-keys = list(unique)
-keys.sort()
-for k in keys:
+for k in sorted(unique):
 print k
 
 



--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-06 Thread noreply

revno: 6541
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-08-05 07:30:30 -0400
message:
  Added a doctest for styles.
added:
  Mailman/docs/styles.txt
modified:
  Mailman/app/styles.py

=== added file 'Mailman/docs/styles.txt'
--- a/Mailman/docs/styles.txt   1970-01-01 00:00:00 +
+++ b/Mailman/docs/styles.txt   2007-08-05 11:30:30 +
@@ -0,0 +1,142 @@
+List styles
+===
+
+List styles are a way to name and apply a canned collection of attribute
+settings.  Every style has a name, which must be unique within the context of
+a specific style manager.  There is usually only one global style manager.
+
+Styles also have a priority, which allows you to specify the order in which
+multiple styles will be applied.  A style has a `match` function which is used
+to determine whether the style should be applied to a particular mailing list
+or not.  And finally, application of a style to a mailing list can really
+modify the mailing list any way it wants.
+
+Let's start with a vanilla mailing list and a default style manager.
+
+ from Mailman.configuration import config
+ from Mailman.database import flush
+ mlist = config.db.list_manager.create('[EMAIL PROTECTED]')
+ from Mailman.app.styles import style_manager
+
+
+The default style
+-
+
+There is a default style which implements the legacy application of list
+defaults from Defaults.py.  This style only matching a mailing list when no
+other styles match, and it has the lowest priority.  The low priority means
+that it is matched last and if it matches, it is applied last.
+
+ default_style = style_manager.get('default')
+ default_style.name
+'default'
+ default_style.priority
+0
+ sorted(style.name for style in style_manager.styles)
+['default']
+
+Given a mailing list, you can ask the style manager to find all the styles
+that match the list.  The registered styles will be sorted by decreasing
+priority and each style's `match()` method will be called in turn.  The sorted
+list of matching styles will be returned -- but not applied -- by the style
+manager's `lookup()` method.
+
+ [style.name for style in style_manager.lookup(mlist)]
+['default']
+
+If the site administrator modified their mailman.cfg file, the default style
+would pick this up and apply it to the mailing list.
+
+ print mlist.msg_footer
+None
+ config.DEFAULT_MSG_FOOTER = u'default footer'
+ default_style.apply(mlist)
+ flush()
+ mlist.msg_footer
+u'default footer'
+
+
+Registering styles
+--
+
+New styles must implement the IStyle interface.
+
+ from zope.interface import implements
+ from Mailman.interfaces import IStyle
+ class TestStyle(object):
+... name = 'test'
+... priority = 10
+... def apply(self, mailing_list):
+... # Just does something very simple.
+... mailing_list.msg_footer = u'test footer'
+... def match(self, mailing_list, styles):
+... # Applies to any test list
+... if 'test' in mailing_list.fqdn_listname:
+... styles.append(self)
+
+You can register a new style with the style manager.
+
+ style_manager.register(TestStyle())
+
+And now if you lookup matching styles, you should find only the new test
+style.  This is because the default style only gets applied when no other
+styles match the mailing list.
+
+ sorted(style.name for style in style_manager.lookup(mlist))
+['test']
+ for style in style_manager.lookup(mlist):
+... style.apply(mlist)
+ flush()
+ mlist.msg_footer
+u'test footer'
+
+
+Style priority
+--
+
+When multiple styles match a particular mailing list, they are applied in
+descending order of priority.  In other words, a priority zero style would be
+applied last.
+
+ class AnotherTestStyle(TestStyle):
+... name = 'another'
+... priority = 5
+... # Use the base class's match() method.
+... def apply(self, mailing_list):
+... mailing_list.msg_footer = u'another footer'
+
+ mlist.msg_footer = u''
+ flush()
+ mlist.msg_footer
+u''
+ style_manager.register(AnotherTestStyle())
+ for style in style_manager.lookup(mlist):
+... style.apply(mlist)
+ flush()
+ mlist.msg_footer
+u'another footer'
+
+You can change the priority of a style, and if you reapply the styles, they
+will take effect in the new priority order.
+
+ style_1 = style_manager.get('test')
+ style_1.priority = 5
+ style_2 = style_manager.get('another')
+ style_2.priority = 10
+ for style in style_manager.lookup(mlist):
+... style.apply(mlist)
+ flush()
+ mlist.msg_footer
+u'test footer'
+
+
+Corner cases
+
+
+If you register a style with the same name as an already registered style, you
+get 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-06 Thread noreply

revno: 6543
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-08-05 22:49:04 -0500
message:
  Fixed a problem where members of a deleted mailing list were hanging around.
  This would cause duplicate members (e.g. owners) if you created, deleted and
  then recreated the mailing list.
  
  Mailman.app.create - Mailman.app.lifecycle; Mailman/doc/create.txt -
  Mailman/doc/lifecycle.txt; also added a remove_list() function.
  
  Added SubscriptionError base class, made HostileSubscriptionError inherit from
  that, and added a new AlreadySubscribedError.
  
  Rewrote bin/rmlist to use the new lifecycle.remove_list() function.
  
  IAddress.subscribe() must now throw an AlreadySubscribedError if the address
  is already subscribed to the mailing list with the given role.
  
  Added a Subscribers roster, attached to the IMailingList which gives access to
  all subscribers of a mailing list, regardless of their role.  Added a new test
  for this roster.
renamed:
  Mailman/app/create.py = Mailman/app/lifecycle.py
  Mailman/docs/create.txt = Mailman/docs/lifecycle.txt
modified:
  Mailman/Errors.py
  Mailman/app/__init__.py*
  Mailman/bin/newlist.py
  Mailman/bin/rmlist.py
  Mailman/database/model/address.py
  Mailman/database/model/mailinglist.py
  Mailman/database/model/roster.py
  Mailman/docs/membership.txt
  Mailman/interfaces/address.py
  Mailman/interfaces/mlistrosters.py
  Mailman/app/lifecycle.py
  Mailman/docs/lifecycle.txt

=== renamed file 'Mailman/app/create.py' = 'Mailman/app/lifecycle.py'
--- a/Mailman/app/create.py 2007-08-05 12:42:16 +
+++ b/Mailman/app/lifecycle.py  2007-08-06 03:49:04 +
@@ -17,6 +17,10 @@
 
 Application level list creation.
 
+import os
+import shutil
+import logging
+
 from Mailman import Errors
 from Mailman import Utils
 from Mailman.Utils import ValidateEmail
@@ -25,6 +29,14 @@
 from Mailman.configuration import config
 from Mailman.constants import MemberRole
 
+__all__ = [
+'create_list',
+'remove_list',
+]
+
+
+log = logging.getLogger('mailman.error')
+
 
 
 def create_list(fqdn_listname, owners=None):
@@ -58,3 +70,47 @@
 addr = list(user.addresses)[0]
 addr.subscribe(mlist, MemberRole.owner)
 return mlist
+
+
+
+def remove_list(fqdn_listname, mailing_list=None, archives=True):
+Remove the list and all associated artifacts and subscriptions.
+removeables = []
+# mailing_list will be None when only residual archives are being removed.
+if mailing_list:
+# Remove all subscriptions, regardless of role.
+for member in mailing_list.subscribers.members:
+member.unsubscribe()
+# Delete the mailing list from the database.
+config.db.list_manager.delete(mailing_list)
+# Do the MTA-specific list deletion tasks
+if config.MTA:
+modname = 'Mailman.MTA.' + config.MTA
+__import__(modname)
+sys.modules[modname].remove(mailing_list)
+# Remove the list directory.
+removeables.append(os.path.join(config.LIST_DATA_DIR, fqdn_listname))
+# Remove any stale locks associated with the list.
+for filename in os.listdir(config.LOCK_DIR):
+fn_listname = filename.split('.')[0]
+if fn_listname == fqdn_listname:
+removeables.append(os.path.join(config.LOCK_DIR, filename))
+if archives:
+private_dir = config.PRIVATE_ARCHIVE_FILE_DIR
+public_dir  = config.PUBLIC_ARCHIVE_FILE_DIR
+removeables.extend([
+os.path.join(private_dir, fqdn_listname),
+os.path.join(private_dir, fqdn_listname + '.mbox'),
+os.path.join(public_dir, fqdn_listname),
+os.path.join(public_dir, fqdn_listname + '.mbox'),
+])
+# Now that we know what files and directories to delete, delete them.
+for target in removeables:
+if os.path.islink(target):
+os.unlink(target)
+elif os.path.isdir(target):
+shutil.rmtree(target)
+elif os.path.isfile(target):
+os.unlink(target)
+else:
+log.error('Could not delete list artifact: $target')

=== renamed file 'Mailman/docs/create.txt' = 'Mailman/docs/lifecycle.txt'
--- a/Mailman/docs/create.txt   2007-08-05 12:42:16 +
+++ b/Mailman/docs/lifecycle.txt2007-08-06 03:49:04 +
@@ -1,12 +1,12 @@
-Application level list creation

-
-The low-level way to create a new mailing list is to use the IListManager
-interface.  This interface simply adds the appropriate database entries to
-record the list's creation.
-
-There is a higher level interface for creating mailing lists which performs a
-few additional tasks such as:
+Application level list lifecycle
+
+
+The low-level way to create and delete a mailing list is to use the
+IListManager interface.  This 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-06 Thread noreply

revno: 6542
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-08-05 08:42:16 -0400
message:
  Added a doctest for the Mailman.app.create module.
  
  BadListNameError is gone.  Use InvalidEmailAddress instead.
  
  Move owner registration from bin/newlist to Mailman/app/create.py, but do not
  verified owner email addresses here.  Eventually we'll hook in the IRegistrar
  stuff for unverified owner addresses.
  
  IStyleManager.register() verifies that its registering an IStyle.
  
  Added IStyleManager.unregister(), along with updated interfaces and doctests.
  
  Clean up all styles except the default one in the system documentation test
  harness.
added:
  Mailman/docs/create.txt
modified:
  Mailman/Cgi/create.py
  Mailman/Errors.py
  Mailman/app/create.py
  Mailman/app/styles.py
  Mailman/bin/newlist.py
  Mailman/docs/styles.txt
  Mailman/interfaces/styles.py
  Mailman/tests/test_documentation.py

=== added file 'Mailman/docs/create.txt'
--- a/Mailman/docs/create.txt   1970-01-01 00:00:00 +
+++ b/Mailman/docs/create.txt   2007-08-05 12:42:16 +
@@ -0,0 +1,123 @@
+Application level list creation
+---
+
+The low-level way to create a new mailing list is to use the IListManager
+interface.  This interface simply adds the appropriate database entries to
+record the list's creation.
+
+There is a higher level interface for creating mailing lists which performs a
+few additional tasks such as:
+
+ * validating the list's posting address (which also serves as the list's
+   fully qualified name);
+ * ensuring that the list's domain is registered;
+ * applying all matching styles to the new list;
+ * creating and assigning list owners;
+ * notifying watchers of list creation;
+ * creating ancillary artifacts (such as the list's on-disk directory)
+
+ from Mailman.app.create import create_list
+
+
+Posting address validation
+--
+
+If you try to use the higher-level interface to create a mailing list with a
+bogus posting address, you get an exception.
+
+ create_list('not a valid address')
+Traceback (most recent call last):
+...
+InvalidEmailAddress: 'not a valid address'
+
+If the posting address is valid, but the domain has not been registered with
+Mailman yet, you get an exception.
+
+ create_list('[EMAIL PROTECTED]')
+Traceback (most recent call last):
+...
+BadDomainSpecificationError: example.org
+
+
+Creating a list applies its styles
+--
+
+Start by registering a test style.
+
+ from zope.interface import implements
+ from Mailman.interfaces import IStyle
+ class TestStyle(object):
+... implements(IStyle)
+... name = 'test'
+... priority = 10
+... def apply(self, mailing_list):
+... # Just does something very simple.
+... mailing_list.msg_footer = u'test footer'
+... def match(self, mailing_list, styles):
+... # Applies to any test list
+... if 'test' in mailing_list.fqdn_listname:
+... styles.append(self)
+ from Mailman.app.styles import style_manager
+ style_manager.register(TestStyle())
+
+Using the higher level interface for creating a list, applies all matching
+list styles.
+
+ mlist_1 = create_list('[EMAIL PROTECTED]')
+ from Mailman.database import flush
+ flush()
+ mlist_1.fqdn_listname
+'[EMAIL PROTECTED]'
+ mlist_1.msg_footer
+u'test footer'
+
+
+Creating a list with owners
+---
+
+You can also specify a list of owner email addresses.  If these addresses are
+not yet known, they will be registered, and new users will be linked to them.
+However the addresses are not verified.
+
+ owners = ['[EMAIL PROTECTED]', '[EMAIL PROTECTED]',
+...   '[EMAIL PROTECTED]', '[EMAIL PROTECTED]']
+ mlist_2 = create_list('[EMAIL PROTECTED]', owners)
+ flush()
+ mlist_2.fqdn_listname
+'[EMAIL PROTECTED]'
+ mlist_2.msg_footer
+u'test footer'
+ sorted(addr.address for addr in mlist_2.owners.addresses)
+['[EMAIL PROTECTED]', '[EMAIL PROTECTED]',
+ '[EMAIL PROTECTED]', '[EMAIL PROTECTED]']
+
+None of the owner addresses are verified.
+
+ any(addr.verified_on is not None for addr in mlist_2.owners.addresses)
+False
+
+However, all addresses are linked to users.
+
+ # The owners have no names yet
+ len(list(mlist_2.owners.users))
+4
+
+If you create a mailing list with owner addresses that are already known to
+the system, they won't be created again.
+
+ from Mailman.configuration import config
+ usermgr = config.db.user_manager
+ user_a = usermgr.get_user('[EMAIL PROTECTED]')
+ user_b = usermgr.get_user('[EMAIL PROTECTED]')
+ user_c = usermgr.get_user('[EMAIL PROTECTED]')
+ user_d = usermgr.get_user('[EMAIL PROTECTED]')
+ 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-02 Thread noreply

revno: 6539
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Thu 2007-08-02 10:47:56 -0400
message:
  Refactor IDatabase interface so that the user_manager, list_manager,
  message_store, and pendings 'databases' hang off the IDatabase object attached
  to the config object.  For IPending, no adaptation is necessary.
modified:
  Mailman/Handlers/Hold.py
  Mailman/Queue/Runner.py
  Mailman/app/registrar.py
  Mailman/database/__init__.py
  Mailman/docs/ack-headers.txt
  Mailman/docs/acknowledge.txt
  Mailman/docs/addresses.txt
  Mailman/docs/after-delivery.txt
  Mailman/docs/antispam.txt
  Mailman/docs/archives.txt
  Mailman/docs/avoid-duplicates.txt
  Mailman/docs/bounces.txt
  Mailman/docs/calc-recips.txt
  Mailman/docs/cleanse.txt
  Mailman/docs/cook-headers.txt
  Mailman/docs/decorate.txt
  Mailman/docs/digests.txt
  Mailman/docs/file-recips.txt
  Mailman/docs/filtering.txt
  Mailman/docs/hold.txt
  Mailman/docs/listmanager.txt
  Mailman/docs/membership.txt
  Mailman/docs/message.txt
  Mailman/docs/messagestore.txt
  Mailman/docs/mlist-addresses.txt
  Mailman/docs/news-runner.txt
  Mailman/docs/nntp.txt
  Mailman/docs/outgoing.txt
  Mailman/docs/pending.txt
  Mailman/docs/registration.txt
  Mailman/docs/reply-to.txt
  Mailman/docs/replybot.txt
  Mailman/docs/runner.txt
  Mailman/docs/scrubber.txt
  Mailman/docs/subject-munging.txt
  Mailman/docs/tagger.txt
  Mailman/docs/usermanager.txt
  Mailman/docs/users.txt
  Mailman/initialize.py
  Mailman/interfaces/database.py
  Mailman/tests/test_documentation.py

=== modified file 'Mailman/Handlers/Hold.py'
--- a/Mailman/Handlers/Hold.py  2007-08-01 21:05:06 +
+++ b/Mailman/Handlers/Hold.py  2007-08-02 14:47:56 +
@@ -42,7 +42,7 @@
 from Mailman import Utils
 from Mailman import i18n
 from Mailman.configuration import config
-from Mailman.interfaces import IPendable, IPending
+from Mailman.interfaces import IPendable
 
 log = logging.getLogger('mailman.vette')
 
@@ -249,7 +249,7 @@
 # bounce processing that might be needed.
 pendable = HeldMessagePendable(type=HeldMessagePendable.PEND_KEY,
id=str(id))
-token = IPending(config.db).add(pendable)
+token = config.db.pendings.add(pendable)
 # Get the language to send the response in.  If the sender is a member,
 # then send it in the member's language, otherwise send it in the mailing
 # list's preferred language.

=== modified file 'Mailman/Queue/Runner.py'
--- a/Mailman/Queue/Runner.py   2007-06-28 05:11:50 +
+++ b/Mailman/Queue/Runner.py   2007-08-02 14:47:56 +
@@ -147,7 +147,7 @@
 #
 # Find out which mailing list this message is destined for.
 listname = msgdata.get('listname')
-mlist = config.list_manager.get(listname)
+mlist = config.db.list_manager.get(listname)
 if not mlist:
 log.error('Dequeuing message destined for missing list: %s',
   listname)

=== modified file 'Mailman/app/registrar.py'
--- a/Mailman/app/registrar.py  2007-08-01 20:11:08 +
+++ b/Mailman/app/registrar.py  2007-08-02 14:47:56 +
@@ -32,7 +32,7 @@
 from Mailman.Utils import ValidateEmail
 from Mailman.configuration import config
 from Mailman.i18n import _
-from Mailman.interfaces import IDomain, IPendable, IPending, IRegistrar
+from Mailman.interfaces import IDomain, IPendable, IRegistrar
 
 __i18n_templates__ = True
 
@@ -57,7 +57,7 @@
 ValidateEmail(address)
 # Check to see if there is already a verified IAddress in the database
 # matching this address.  If so, there's nothing to do.
-usermgr = config.user_manager
+usermgr = config.db.user_manager
 addr = usermgr.get_address(address)
 if addr and addr.verified_on:
 # Before returning, see if this address is linked to a user.  If
@@ -73,8 +73,7 @@
 pendable = PendableRegistration(type=PendableRegistration.PEND_KEY,
 address=address,
 real_name=real_name)
-pendingdb = IPending(config.db)
-token = pendingdb.add(pendable)
+token = config.db.pendings.add(pendable)
 # Set up some local variables for translation interpolation.
 domain = IDomain(self._context)
 domain_name = _(domain.domain_name)
@@ -96,8 +95,7 @@
 def confirm(self, token):
 See `IUserRegistrar`.
 # For convenience
-pendingdb = IPending(config.db)
-pendable = pendingdb.confirm(token)
+pendable = config.db.pendings.confirm(token)
 if pendable is None:
 return False
 missing = object()
@@ -114,7 +112,7 @@
 # We are going to end up with an IAddress for the verified address
 # and an IUser linked to this IAddress.  See if any of these objects
 # currently exist in our database.
-

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-01 Thread noreply

revno: 6536
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Mon 2007-07-23 17:52:39 -0400
message:
  Remove an empty file
removed:
  Mailman/Site.py

=== removed file 'Mailman/Site.py'


--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-01 Thread noreply
1 revision was removed from the branch.


--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-01 Thread noreply

revno: 6538
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Wed 2007-08-01 17:05:06 -0400
message:
  Repair some test suite regressions.
modified:
  Mailman/Handlers/Hold.py
  Mailman/LockFile.py
  Mailman/Message.py
  Mailman/Queue/NewsRunner.py
  Mailman/docs/addresses.txt
  Mailman/docs/hold.txt
  Mailman/docs/news-runner.txt
  Mailman/docs/pending.txt

=== modified file 'Mailman/Handlers/Hold.py'
--- a/Mailman/Handlers/Hold.py  2007-08-01 20:11:08 +
+++ b/Mailman/Handlers/Hold.py  2007-08-01 21:05:06 +
@@ -35,12 +35,14 @@
 from email.mime.message import MIMEMessage
 from email.mime.text import MIMEText
 from types import ClassType
+from zope.interface import implements
 
 from Mailman import Errors
 from Mailman import Message
-from Mailman import Pending
 from Mailman import Utils
 from Mailman import i18n
+from Mailman.configuration import config
+from Mailman.interfaces import IPendable, IPending
 
 log = logging.getLogger('mailman.vette')
 
@@ -129,6 +131,12 @@
 
 
 
+class HeldMessagePendable(dict):
+implements(IPendable)
+PEND_KEY = 'held message'
+
+
+
 def process(mlist, msg, msgdata):
 if msgdata.get('approved'):
 return
@@ -239,7 +247,9 @@
 #
 # This message should appear to come from list-admin so as to handle any
 # bounce processing that might be needed.
-cookie = mlist.pend_new(Pending.HELD_MESSAGE, id)
+pendable = HeldMessagePendable(type=HeldMessagePendable.PEND_KEY,
+   id=str(id))
+token = IPending(config.db).add(pendable)
 # Get the language to send the response in.  If the sender is a member,
 # then send it in the member's language, otherwise send it in the mailing
 # list's preferred language.
@@ -247,9 +257,9 @@
 lang = (member.preferred_language if member else mlist.preferred_language)
 if not fromusenet and ackp(msg) and mlist.respond_to_post_requests and \
mlist.autorespondToSender(sender, lang):
-# Get a confirmation cookie
+# Get a confirmation token
 d['confirmurl'] = '%s/%s' % (mlist.GetScriptURL('confirm', absolute=1),
- cookie)
+ token)
 lang = msgdata.get('lang', lang)
 subject = _('Your message to $listname awaits moderator approval')
 text = Utils.maketext('postheld.txt', d, lang=lang, mlist=mlist)
@@ -284,7 +294,7 @@
 message will be approved for posting to the list.  The Approved: header can
 also appear in the first line of the body of the reply.)),
 _charset=Utils.GetCharSet(lang))
-dmsg['Subject'] = 'confirm ' + cookie
+dmsg['Subject'] = 'confirm ' + token
 dmsg['Sender'] = requestaddr
 dmsg['From'] = requestaddr
 dmsg['Date'] = email.utils.formatdate(localtime=True)

=== modified file 'Mailman/LockFile.py'
--- a/Mailman/LockFile.py   2007-05-28 20:21:41 +
+++ b/Mailman/LockFile.py   2007-08-01 21:05:06 +
@@ -59,12 +59,13 @@
 import random
 import socket
 import logging
+import datetime
 import traceback
 
 # Units are floating-point seconds.
-DEFAULT_LOCK_LIFETIME  = 15
+DEFAULT_LOCK_LIFETIME  = datetime.timedelta(seconds=15)
 # Allowable a bit of clock skew
-CLOCK_SLOP = 10
+CLOCK_SLOP = datetime.timedelta(seconds=10)
 # This is appropriate for Mailman, but you may want to change this if you're
 # using this code outside Mailman.
 log = logging.getLogger('mailman.locks')
@@ -95,8 +96,8 @@
 Create the resource lock using lockfile as the global lock file.  Each
 process laying claim to this resource lock will create their own
 temporary lock files based on the path specified by lockfile.
-Optional lifetime is the number of seconds the process expects to hold
-the lock.
+Optional lifetime is a timedelta specifying the number of seconds the
+process expects to hold the lock.
 
 set_lifetime(lifetime):
 Set a new lock lifetime.  This takes affect the next time the file is
@@ -155,7 +156,7 @@
 self._owned = True
 
 def __repr__(self):
-return 'LockFile %s: %s [%s: %ssec] pid=%s' % (
+return 'LockFile %s: %s [%s: %s] pid=%s' % (
 id(self), self._lockfile,
 self.locked() and 'locked' or 'unlocked',
 self._lifetime, os.getpid())
@@ -400,7 +401,8 @@
 return None
 
 def _touch(self, filename=None):
-t = time.time() + self._lifetime
+expiration_date = datetime.datetime.now() + self._lifetime
+t = time.mktime(expiration_date.timetuple())
 try:
 # XXX We probably don't need to modify atime, but this is easier.
 os.utime(filename or self._tmpfname, (t, t))

=== modified file 'Mailman/Message.py'
--- a/Mailman/Message.py2007-08-01 20:11:08 +
+++ 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-01 Thread noreply

revno: 6534
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-07-22 19:52:34 -0400
message:
  The start of a message store definition.  Whether this will end up being used
  for the archive or not is left to be seen.
  
  Define an interface, test, and implementation of a basic message store using
  globally unique identifiers of the form: archive/hash/seqno
  
  - archive is the base url of the archive, e.g. http://archives.example.com.
This is available in the List-Archive header.
  - hash is the base32 encoded sha1 hash of the message's Message-ID and Date
headers, which it must have.  This is available in the X-List-ID-Hash
header.
  - seqno is a sequence number specific to the archive which will uniquely
identify the message should there be a Message-ID/Date collision.  this is
available in the X-List-Sequence-Number header.
  
  Added a MESSAGES_DIR variable to the config.
  
  Added a .message_store attribute to the config.
added:
  Mailman/database/messagestore.py
  Mailman/database/model/message.py
  Mailman/docs/messagestore.txt
  Mailman/interfaces/messagestore.py
modified:
  Mailman/configuration.py
  Mailman/database/__init__.py
  Mailman/database/model/__init__.py
  Mailman/docs/archives.txt

=== added file 'Mailman/database/messagestore.py'
--- a/Mailman/database/messagestore.py  1970-01-01 00:00:00 +
+++ b/Mailman/database/messagestore.py  2007-07-22 23:52:34 +
@@ -0,0 +1,140 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+from __future__ import with_statement
+
+__metaclass__ = type
+__all__ = [
+'MessageStore',
+]
+
+import os
+import errno
+import base64
+import hashlib
+import cPickle as pickle
+
+from zope.interface import implements
+
+from Mailman import Utils
+from Mailman.configuration import config
+from Mailman.database.model import Message
+from Mailman.interfaces import IMessageStore
+
+# It could be very bad if you have already stored files and you change this
+# value.  We'd need a script to reshuffle and resplit.
+MAX_SPLITS = 2
+EMPTYSTRING = ''
+
+
+
+class MessageStore:
+implements(IMessageStore)
+
+def add(self, message):
+# Ensure that the message has the requisite headers.
+message_ids = message.get_all('message-id', [])
+dates = message.get_all('date', [])
+if not (len(message_ids) == 1 and len(dates) == 1):
+raise ValueError(
+'Exactly one Message-ID and one Date header required')
+# Calculate and insert the X-List-ID-Hash.
+message_id = message_ids[0]
+date = dates[0]
+shaobj = hashlib.sha1(message_id)
+shaobj.update(date)
+hash32 = base64.b32encode(shaobj.digest())
+del message['X-List-ID-Hash']
+message['X-List-ID-Hash'] = hash32
+# Calculate the path on disk where we're going to store this message
+# object, in pickled format.
+parts = []
+split = list(hash32)
+while split and len(parts)  MAX_SPLITS:
+parts.append(split.pop(0) + split.pop(0))
+parts.append(EMPTYSTRING.join(split))
+relpath = os.path.join(*parts)
+# Store the message in the database.  This relies on the database
+# providing a unique serial number, but to get this information, we
+# have to use a straight insert instead of relying on Elixir to create
+# the object.
+result = Message.table.insert().execute(
+hash=hash32, path=relpath, message_id=message_id)
+# Add the additional header.
+seqno = result.last_inserted_ids()[0]
+del message['X-List-Sequence-Number']
+message['X-List-Sequence-Number'] = str(seqno)
+# Now calculate the full file system path.
+path = os.path.join(config.MESSAGES_DIR, relpath, str(seqno))
+# Write the file to the path, but catch the appropriate exception in
+# case the parent directories don't yet exist.  In that case, create
+# them and try again.
+while True:
+try:
+with open(path, 'w') as fp:
+# -1 says to use the highest protocol available.
+ 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-08-01 Thread noreply

revno: 6535
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Mon 2007-07-23 01:57:48 -0400
message:
  Remove last vestiges of dbcontext
removed:
  Mailman/database/dbcontext.py
modified:
  Mailman/bin/testall.py

=== removed file 'Mailman/database/dbcontext.py'
--- a/Mailman/database/dbcontext.py 2007-05-28 20:21:41 +
+++ b/Mailman/database/dbcontext.py 1970-01-01 00:00:00 +
@@ -1,174 +0,0 @@
-# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-import os
-import sys
-import logging
-import weakref
-
-from elixir import create_all, metadata, objectstore
-from sqlalchemy import create_engine
-from string import Template
-from urlparse import urlparse
-
-from Mailman import Version
-from Mailman.configuration import config
-from Mailman.database.txnsupport import txn
-
-
-
-class MlistRef(weakref.ref):
-def __init__(self, mlist, callback):
-super(MlistRef, self).__init__(mlist, callback)
-self.fqdn_listname = mlist.fqdn_listname
-
-
-
-class DBContext(object):
-def __init__(self):
-# Special transaction used only for MailList.Lock() .Save() and
-# .Unlock() interface.
-self._mlist_txns = {}
-
-def connect(self):
-# Calculate the engine url
-url = Template(config.SQLALCHEMY_ENGINE_URL).safe_substitute(
-config.paths)
-# XXX By design of SQLite, database file creation does not honor
-# umask.  See their ticket #1193:
-# http://www.sqlite.org/cvstrac/tktview?tn=1193,31
-#
-# This sucks for us because the mailman.db file /must/ be group
-# writable, however even though we guarantee our umask is 002 here, it
-# still gets created without the necessary g+w permission, due to
-# SQLite's policy.  This should only affect SQLite engines because its
-# the only one that creates a little file on the local file system.
-# This kludges around their bug by touching the database file before
-# SQLite has any chance to create it, thus honoring the umask and
-# ensuring the right permissions.  We only try to do this for SQLite
-# engines, and yes, we could have chmod'd the file after the fact, but
-# half dozen and all...
-self._touch(url)
-engine = create_engine(url)
-engine.echo = config.SQLALCHEMY_ECHO
-metadata.connect(engine)
-# Load and create the Elixir active records.  This works by
-# side-effect.
-import Mailman.database.model
-create_all()
-# Validate schema version.
-v = Mailman.database.model.Version.get_by(component='schema')
-if not v:
-# Database has not yet been initialized
-v = Mailman.database.model.Version(
-component='schema',
-version=Version.DATABASE_SCHEMA_VERSION)
-objectstore.flush()
-elif v.version  Version.DATABASE_SCHEMA_VERSION:
-# XXX Update schema
-raise SchemaVersionMismatchError(v.version)
-
-def _touch(self, url):
-parts = urlparse(url)
-if parts.scheme  'sqlite':
-return
-path = os.path.normpath(parts.path)
-fd = os.open(path, os.O_WRONLY |  os.O_NONBLOCK | os.O_CREAT, 0666)
-# Ignore errors
-if fd  0:
-os.close(fd)
-
-# Cooperative method for use with @txn decorator
-def _withtxn(self, meth, *args, **kws):
-try:
-txn = objectstore.session.current.create_transaction()
-rtn = meth(*args, **kws)
-except:
-txn.rollback()
-raise
-else:
-txn.commit()
-return rtn
-
-def _unlock_mref(self, mref):
-txn = self._mlist_txns.pop(mref.fqdn_listname, None)
-if txn is not None:
-txn.rollback()
-
-# Higher level interface
-def api_lock(self, mlist):
-# Don't try to re-lock a list
-if mlist.fqdn_listname in self._mlist_txns:
-return
-txn = objectstore.session.current.create_transaction()
-mref = MlistRef(mlist, self._unlock_mref)
-# If 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-24 Thread noreply

revno: 6534
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: entrypoints
timestamp: Tue 2007-07-24 18:45:25 -0400
message:
  Add setuptools plug-in entry point for defining different database backends.
  Now someone could distribute a setuptools package that provided say, a MySQL
  database implementation and very easily override the stock database.  How
  awesome is setuptools?
  
  Removed MANAGERS_INIT_FUNCTION since setuptools gives us a much more standard
  way of defining this plug-in entry point.  Remove other old crud from
  Defaults.py.
  
  Restructure our own 'stock' database backend to be a plugin so it's totally on
  par with any other package.  The only special case is that if more than one
  such entry point is defined, we filter out the 'stock' one (i.e. ours) under
  the assumption that the user is overriding it.  If we still have more than one
  plug-in, it's an error.
  
  Restructure the initialization subsystem to use the plug-in, doing all the
  proper assertions and what not.  The IDatabase interface defines what the
  database back-end plugin must provide.  I've no doubt this will eventually
  need a bit more fleshing out, but it gives all this stuff a principled hook
  point instead of something ad-hoc.
added:
  Mailman/interfaces/database.py
modified:
  Mailman/Defaults.py
  Mailman/database/__init__.py
  Mailman/initialize.py
  setup.py

=== added file 'Mailman/interfaces/database.py'
--- a/Mailman/interfaces/database.py1970-01-01 00:00:00 +
+++ b/Mailman/interfaces/database.py2007-07-24 22:45:25 +
@@ -0,0 +1,48 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+Interfaces for database interaction.
+
+By providing an object with this interface and declaring it in a package
+setup.py file as an entry point in the 'mailman.database' group with the name
+'initializer', you can distribute entirely different database layers for
+Mailman's back end.
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IDatabase(Interface):
+Database layer interface.
+
+def initialize():
+Initialize the database layer, using whatever means necessary.
+
+def flush():
+Flush current database changes.
+
+# XXX Eventually we probably need to support a transaction manager
+# interface, e.g. begin(), commit(), abort().  We will probably also need
+# to support a shutdown() method for cleanly disconnecting from the
+# database.sy
+
+list_manager = Attribute(
+The IListManager instance provided by the database layer.)
+
+user_manager = Attribute(
+The IUserManager instance provided by the database layer.)

=== modified file 'Mailman/Defaults.py'
--- a/Mailman/Defaults.py   2007-07-17 03:55:49 +
+++ b/Mailman/Defaults.py   2007-07-24 22:45:25 +
@@ -1,5 +1,3 @@
-# -*- python -*-
-
 # Copyright (C) 1998-2007 by the Free Software Foundation, Inc.
 #
 # This program is free software; you can redistribute it and/or
@@ -19,11 +17,6 @@
 
 Distributed default settings for significant Mailman config variables.
 
-# NEVER make site configuration changes to this file.  ALWAYS make them in
-# mm_cfg.py instead, in the designated area.  See the comments in that file
-# for details.
-
-
 import os
 
 from munepy import Enum
@@ -110,11 +103,6 @@
 # Database options
 #
 
-# Initialization function for creating the IListManager, IUserManager, and
-# IMessageManager objects, as a Python dotted name.  This function must take
-# zero arguments.
-MANAGERS_INIT_FUNCTION = 'Mailman.database.initialize'
-
 # Use this to set the SQLAlchemy database engine URL.  You generally have one
 # primary database connection for all of Mailman.  List data and most rosters
 # will store their data in this database, although external rosters may access

=== modified file 'Mailman/database/__init__.py'
--- a/Mailman/database/__init__.py  2007-05-28 20:21:41 +
+++ b/Mailman/database/__init__.py  2007-07-24 22:45:25 +
@@ -20,30 +20,45 @@
 import os
 
 from elixir import objectstore
+from zope.interface import implements
 
+from Mailman.interfaces import IDatabase
 from Mailman.database.listmanager import ListManager
 from 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-21 Thread noreply

revno: 6532
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sat 2007-07-21 16:19:54 -0400
message:
  Merge the setuptools branch.  Fix a couple of test modules that are
  disabled anyway.
removed:
  Mailman/Archiver/Makefile.in
  Mailman/Bouncers/Makefile.in
  Mailman/Cgi/Makefile.in
  Mailman/Commands/Makefile.in
  Mailman/Gui/Makefile.in
  Mailman/Handlers/Makefile.in
  Mailman/MTA/Makefile.in
  Mailman/Makefile.in
  Mailman/Queue/Makefile.in
  Mailman/Queue/tests/Makefile.in
  Mailman/bin/Makefile.in
  Mailman/database/Makefile.in
  Mailman/database/model/Makefile.in
  Mailman/docs/Makefile.in
  Mailman/ext/Makefile.in
  Mailman/interfaces/Makefile.in
  Mailman/interfaces/manager.py
  Mailman/testing/Makefile.in
  Mailman/testing/base.py
  Mailman/testing/bounces/Makefile.in
  Makefile.in
  bin/Makefile.in
  bin/check_db
  bin/convert.py
  bin/mailmanctl
  bin/mmshell
  bin/qrunner
  configure
  configure.in
  cron/Makefile.in
  install-sh
  messages/Makefile.in
  misc/Elixir-0.3.0.tar.gz
  misc/Makefile.in
  misc/SQLAlchemy-0.3.3.tar.gz
  misc/munepy-1.1-py2.5.egg
  misc/pysqlite-2.3.2.tar.gz
  misc/setuptools-0.6c3.tar.gz
  misc/wsgiref-0.1.2-py2.4.egg
  misc/zope.interface-3.3.0.1.tar.gz
  mkinstalldirs
  scripts/Makefile.in
  src/
  src/Makefile.in
  src/cgi-wrapper.c
  src/common.c
  src/common.h
  src/mail-wrapper.c
  src/vsnprintf.c
  templates/Makefile.in
  tests/Makefile.in
  tests/msgs/Makefile.in
added:
  MANIFEST.in
  Mailman/bin/make_instance.py
  Mailman/data/__init__.py
  Mailman/docs/languages.txt
  Mailman/interfaces/languages.py
  Mailman/languages.py
  Mailman/messages/__init__.py
  Mailman/templates/__init__.py
  Mailman/tests/bounces/__init__.py
  ez_setup.py
  setup.py
renamed:
  Mailman/Defaults.py.in = Mailman/Defaults.py
  Mailman/testing = Mailman/tests
  bin/cleanarch = Mailman/bin/cleanarch.py
  messages = Mailman/messages
  misc = Mailman/data
  misc/mailman.cfg.sample = Mailman/data/mailman.cfg.in
  templates = Mailman/templates
modified:
  .bzrignore
  Mailman/Archiver/Archiver.py
  Mailman/Archiver/HyperArch.py
  Mailman/Cgi/admin.py
  Mailman/Cgi/confirm.py
  Mailman/Cgi/create.py
  Mailman/Cgi/listinfo.py
  Mailman/Cgi/options.py
  Mailman/Cgi/rmlist.py
  Mailman/Cgi/roster.py
  Mailman/Cgi/subscribe.py
  Mailman/Commands/cmd_confirm.py
  Mailman/Commands/cmd_help.py
  Mailman/Commands/cmd_lists.py
  Mailman/Commands/cmd_password.py
  Mailman/Commands/cmd_set.py
  Mailman/Commands/cmd_who.py
  Mailman/Deliverer.py
  Mailman/Gui/Archive.py
  Mailman/Gui/Autoresponse.py
  Mailman/Gui/Bounce.py
  Mailman/Gui/ContentFilter.py
  Mailman/Gui/Digest.py
  Mailman/Gui/General.py
  Mailman/Gui/Language.py
  Mailman/Gui/Privacy.py
  Mailman/Gui/Topics.py
  Mailman/Gui/Usenet.py
  Mailman/HTMLFormatter.py
  Mailman/MailList.py
  Mailman/Mailbox.py
  Mailman/OldStyleMemberships.py
  Mailman/Post.py
  Mailman/SafeDict.py
  Mailman/Utils.py
  Mailman/Version.py
  Mailman/bin/__init__.py
  Mailman/bin/genaliases.py
  Mailman/bin/mailmanctl.py
  Mailman/bin/newlist.py
  Mailman/bin/rmlist.py
  Mailman/bin/testall.py
  Mailman/bin/withlist.py
  Mailman/configuration.py
  Mailman/docs/acknowledge.txt
  Mailman/docs/digests.txt
  Mailman/i18n.py
  Mailman/messages/fr/LC_MESSAGES/mailman.po
  Mailman/tests/emailbase.py
  Mailman/tests/test_bounces.py
  Mailman/tests/test_handlers.py
  Mailman/tests/test_membership.py
  Mailman/tests/test_security_mgr.py
  Mailman/tests/testing.cfg.in
  Mailman/versions.py
  Mailman/Defaults.py
  Mailman/bin/cleanarch.py
  Mailman/data/mailman.cfg.in

revno: 6530.1.10
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: setup
timestamp: Sat 2007-07-21 14:52:50 -0400
message:
  Remove some obsolete command line scripts:
  
  - bin/check_dbs because there are no more pickles
  - bin/convert because in MM3 everything will use $-strings and we'll do 
the
conversion on import of the old list data
  - bin/mmshell because setuptools provides its own equivalent
  - bin/qrunner and bin/mailmanctl because the files were empty
  
  There are a bunch of scripts still left in bin/ which will eventually get
  removed.  I'm leaving them for now because they're either helpers 
generally
  tangential to Mailman (msgfmt, po2template, pygettext, templ2pot, 
transcheck),
  or are using old interfaces that will go away soon (clone_member, convert,
  discard, fix_url, list_admins, remove_members, reset_pw, sync_members).
  
  Also moved bin/cleanarch into Mailman/bin/cleanarch.py and updated it to 
use
  optparse.
  
  Also added a small patch to genalias to standardize its help printing.

revno: 6530.1.9
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-21 Thread noreply

revno: 6533
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sat 2007-07-21 16:34:44 -0400
message:
  Some updates
modified:
  README.txt

=== modified file 'README.txt'
--- a/README.txt2007-05-08 14:46:15 +
+++ b/README.txt2007-07-21 20:34:44 +
@@ -10,7 +10,11 @@
 case second `m'.  Any other spelling is incorrect.
 
 Mailman is written primarily in Python, a free object-oriented scripting
-language.  There is some ANSI C code for security purposes.
+language.  Python is available for all platforms that Mailman is supported
+on, which includes GNU/Linux and most other Unix-like operating systems
+(e.g. Solaris, *BSD, MacOSX, etc.).  It does not run on Windows, although
+web and mail clients on any platform should be able to interact with
+Mailman just fine.
 
 Mailman was originally developed by John Viega.  Subsequent development
 (through version 1.0b3) was by Ken Manheimer.  Further work towards the
@@ -19,6 +23,8 @@
 Version 1.0 and beyond have been primarily maintained by Barry Warsaw with
 contributions from many; see the ACKNOWLEDGMENTS file for details.  Jeremy
 Hylton helped considerably with the Pipermail code in Mailman 2.0.
+Mailman 2.1 is now being primarily maintained by Mark Sapiro and Tokio
+Kikuchi.  Barry Warsaw is the lead developer on Mailman 3.
 
 The Mailman home page is:
 
@@ -29,6 +35,10 @@
 http://www.gnu.org/software/mailman
 http://mailman.sf.net
 
+You might also be interested in the Mailman wiki at:
+
+http://wiki.list.org
+
 Mailman 3.0 requires Python 2.5 or greater, which can be downloaded from:
 
 http://www.python.org
@@ -36,12 +46,6 @@
 It is recommended that you use at least Python 2.5.1, the latest release
 as of this writing (08-May-2007).
 
-You will need an ANSI C compiler to build both Python and Mailman; gcc
-(the GNU C compiler) works just fine.  Mailman currently works only on
-GNU/Linux and other Unix-like operating systems (e.g. Solaris, *BSD,
-MacOSX, etc.).  It does not run on Windows, although web and mail clients
-on any platform should be able to interact with Mailman just fine.
-
 
 FEATURES
 
@@ -65,7 +69,7 @@
 
 - Integrated auto-replies.
 
-- Majordomo-style email based commands.
+- Email commands.
 
 - Integrated bounce detection within an extensible framework.
 
@@ -85,8 +89,7 @@
 mailing lists.
 
 Mailman works with any web server that supports CGI/1.1.  The HTML it
-generates is quite pedestrian and stingy on the graphics so it should be
-friendly to most web browsers and network connections.
+generates should be friendly to most web browsers and network connections.
 
 You will need root access on the machine hosting your Mailman installation
 in order to complete some of the configuration steps.  See the INSTALL.txt



--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-17 Thread noreply

revno: 6531
committer: Mark Sapiro [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Tue 2007-07-17 11:00:34 -0700
message:
  Detect 'who' with 1 or 2 arguments as administrivia.
modified:
  Mailman/Utils.py

=== modified file 'Mailman/Utils.py'
--- a/Mailman/Utils.py  2007-06-18 14:50:23 +
+++ b/Mailman/Utils.py  2007-07-17 18:00:34 +
@@ -538,7 +538,7 @@
 'set': (3, 3),
 'subscribe':   (0, 3),
 'unsubscribe': (0, 1),
-'who': (0, 0),
+'who': (0, 2),
 }
 
 # Given a Message.Message object, test for administrivia (eg subscribe,



--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-11 Thread noreply

revno: 6528
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Wed 2007-07-11 07:06:34 -0400
message:
  Convert ToArchive tests to doctests and do a minimal amount of handler module
  cleanup (really, not much was necessary).
added:
  Mailman/docs/archives.txt
modified:
  Mailman/Handlers/ToArchive.py
  Mailman/testing/test_handlers.py

=== added file 'Mailman/docs/archives.txt'
--- a/Mailman/docs/archives.txt 1970-01-01 00:00:00 +
+++ b/Mailman/docs/archives.txt 2007-07-11 11:06:34 +
@@ -0,0 +1,141 @@
+Archives
+
+
+Updating the archives with posted messages is handled by a separate queue,
+which allows for better memory management and prevents blocking the main
+delivery processes while messages are archived.  This also allows external
+archivers to work in a separate process from the main Mailman delivery
+processes.
+
+ from Mailman.Handlers.ToArchive import process
+ from Mailman.Message import Message
+ from Mailman.Queue.Switchboard import Switchboard
+ from Mailman.configuration import config
+ from Mailman.database import flush
+ from email import message_from_string
+ mlist = config.list_manager.create('[EMAIL PROTECTED]')
+ mlist.preferred_language = 'en'
+ flush()
+ switchboard = Switchboard(config.ARCHQUEUE_DIR)
+
+A helper function.
+
+ def clear():
+... for filebase in switchboard.files:
+... msg, msgdata = switchboard.dequeue(filebase)
+... switchboard.finish(filebase)
+
+
+The purpose of the ToArchive handler is to make a simple decision as to
+whether the message should get archived and if so, to drop the message in the
+archiving queue.  Really the most important things are to determine when a
+message should /not/ get archived.
+
+For example, no digests should ever get archived.
+
+ mlist.archive = True
+ flush()
+ msg = message_from_string(\
+... Subject: A sample message
+...
+... A message of great import.
+... , Message)
+ process(mlist, msg, dict(isdigest=True))
+ switchboard.files
+[]
+
+If the mailing list is not configured to archive, then even regular deliveries
+won't be archived.
+
+ mlist.archive = False
+ flush()
+ process(mlist, msg, {})
+ switchboard.files
+[]
+
+There are two de-facto standards for a message to indicate that it does not
+want to be archived.  We've seen both in the wild so both are supported.  The
+X-No-Archive: header can be used to indicate that the message should not be
+archived.  Confusingly, this header's value is actually ignored.
+
+ mlist.archive = True
+ flush()
+ msg = message_from_string(\
+... Subject: A sample message
+... X-No-Archive: YES
+...
+... A message of great import.
+... , Message)
+ process(mlist, msg, dict(isdigest=True))
+ switchboard.files
+[]
+
+Even a 'no' value will stop the archiving of the message.
+
+ msg = message_from_string(\
+... Subject: A sample message
+... X-No-Archive: No
+...
+... A message of great import.
+... , Message)
+ process(mlist, msg, dict(isdigest=True))
+ switchboard.files
+[]
+
+Another header that's been observed is the X-Archive: header.  Here, the
+header's case folded value must be 'no' in order to prevent archiving.
+
+ msg = message_from_string(\
+... Subject: A sample message
+... X-Archive: No
+...
+... A message of great import.
+... , Message)
+ process(mlist, msg, dict(isdigest=True))
+ switchboard.files
+[]
+
+But if the value is 'yes', then the message will be archived.
+
+ msg = message_from_string(\
+... Subject: A sample message
+... X-Archive: Yes
+...
+... A message of great import.
+... , Message)
+ process(mlist, msg, {})
+ len(switchboard.files)
+1
+ filebase = switchboard.files[0]
+ qmsg, qdata = switchboard.dequeue(filebase)
+ switchboard.finish(filebase)
+ print qmsg.as_string()
+Subject: A sample message
+X-Archive: Yes
+BLANKLINE
+A message of great import.
+BLANKLINE
+ sorted(qdata.items())
+[('_parsemsg', False), ('received_time', ...), ('version', 3)]
+
+Without either archiving header, and all other things being the same, the
+message will get archived.
+
+ msg = message_from_string(\
+... Subject: A sample message
+...
+... A message of great import.
+... , Message)
+ process(mlist, msg, {})
+ len(switchboard.files)
+1
+ filebase = switchboard.files[0]
+ qmsg, qdata = switchboard.dequeue(filebase)
+ switchboard.finish(filebase)
+ print qmsg.as_string()
+Subject: A sample message
+BLANKLINE
+A message of great import.
+BLANKLINE
+ sorted(qdata.items())
+[('_parsemsg', False), ('received_time', ...), ('version', 3)]

=== modified file 'Mailman/Handlers/ToArchive.py'
--- 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-11 Thread noreply

revno: 6529
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Thu 2007-07-12 00:12:45 -0400
message:
  Convert the Scrubber test to a doctest, and fix Scrubber.py, but otherwise
  don't modernize the Scrubber handler.
  
  The is the last of the handler test conversions until we figure out what to do
  with the Approve handler.  In a unified user database the semantics of this
  are unclear.
added:
  Mailman/docs/scrubber.txt
modified:
  Mailman/Handlers/Scrubber.py
  Mailman/testing/test_handlers.py

=== added file 'Mailman/docs/scrubber.txt'
--- a/Mailman/docs/scrubber.txt 1970-01-01 00:00:00 +
+++ b/Mailman/docs/scrubber.txt 2007-07-12 04:12:45 +
@@ -0,0 +1,217 @@
+The scrubber
+
+
+The scrubber is an integral part of Mailman, both in the normal delivery of
+messages and in components such as the archiver.  Its primary purpose is to
+scrub attachments from messages so that binary goop doesn't end up in an
+archive message.
+
+ from Mailman.Handlers.Scrubber import process, save_attachment
+ from Mailman.Message import Message
+ from Mailman.configuration import config
+ from Mailman.database import flush
+ from email import message_from_string
+ mlist = config.list_manager.create('[EMAIL PROTECTED]')
+ mlist.preferred_language = 'en'
+ flush()
+
+Helper functions for getting the attachment data.
+
+ import os, re
+ def read_attachment(filename, remove=True):
+... path = os.path.join(mlist.archive_dir(), filename)
+... fp = open(path)
+... try:
+... data = fp.read()
+... finally:
+... fp.close()
+... if remove:
+... os.unlink(path)
+... return data
+
+ from urlparse import urlparse
+ def read_url_from_message(msg):
+... url = None
+... for line in msg.get_payload().splitlines():
+... mo = re.match('URL: (?Purl[^]+)', line)
+... if mo:
+... url = mo.group('url')
+... break
+... path = '/'.join(urlparse(url).path.split('/')[3:])
+... return read_attachment(path)
+
+
+Saving attachments
+--
+
+The Scrubber handler exposes a function called save_attachments() which can be
+used to strip various types of attachments and store them in the archive
+directory.  This is a public interface used by components outside the normal
+processing pipeline.
+
+Site administrators can decide whether the scrubber should use the attachment
+filename suggested in the message's Content-Disposition: header or not.  If
+enabled, the filename will be used when this header attribute is present (yes,
+this is an unfortunate double negative).
+
+ config.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME = False
+ msg = message_from_string(\
+... Content-Type: image/gif; name=xtest.gif
+... Content-Transfer-Encoding: base64
+... Content-Disposition: attachment; filename=xtest.gif
+... 
+... R0lGODdhAQABAIAAACwAAQABAAACAQUAOw==
+... , Message)
+ save_attachment(mlist, msg, '')
+'http://www.example.com/pipermail/[EMAIL PROTECTED]//xtest.gif'
+ data = read_attachment('xtest.gif')
+ data[:6]
+'GIF87a'
+ len(data)
+34
+
+Saving the attachment does not alter the original message.
+
+ print msg.as_string()
+Content-Type: image/gif; name=xtest.gif
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename=xtest.gif
+BLANKLINE
+R0lGODdhAQABAIAAACwAAQABAAACAQUAOw==
+
+The site administrator can also configure Mailman to ignore the
+Content-Disposition: filename.  This is the default for reasons described in
+the Defaults.py.in file.
+
+ config.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME = True
+ msg = message_from_string(\
+... Content-Type: image/gif; name=xtest.gif
+... Content-Transfer-Encoding: base64
+... Content-Disposition: attachment; filename=xtest.gif
+... 
+... R0lGODdhAQABAIAAACwAAQABAAACAQUAOw==
+... , Message)
+ save_attachment(mlist, msg, '')
+'http://www.example.com/pipermail/[EMAIL PROTECTED]/.../attachment.gif'
+ data = read_attachment('xtest.gif')
+Traceback (most recent call last):
+IOError: [Errno ...] No such file or directory:
+'.../archives/private/[EMAIL PROTECTED]/xtest.gif'
+ data = read_attachment('attachment.gif')
+ data[:6]
+'GIF87a'
+ len(data)
+34
+
+
+Scrubbing image attachments
+---
+
+When scrubbing image attachments, the original message is modified to include
+a reference to the attachment file as available through the on-line archive.
+
+ msg = message_from_string(\
+... MIME-Version: 1.0
+... Content-Type: multipart/mixed; boundary=BOUNDARY
+...
+... --BOUNDARY
+... Content-type: text/plain; charset=us-ascii
+... 
+... This 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-11 Thread noreply

revno: 6530
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Thu 2007-07-12 00:22:33 -0400
message:
  Last bit of existing test suite cleanup for now.  I've removed the inmemory.py
  module since we're not using that any more.  I've also disabled the remaining
  failing tests in test_handlers and test_security_mgr.  Because of the unified
  user database, both of these modules will change significantly, but I don't
  want to remove them just yet.
removed:
  Mailman/testing/inmemory.py
modified:
  Mailman/testing/test_handlers.py
  Mailman/testing/test_security_mgr.py

=== removed file 'Mailman/testing/inmemory.py'
--- a/Mailman/testing/inmemory.py   2007-05-28 20:21:41 +
+++ b/Mailman/testing/inmemory.py   1970-01-01 00:00:00 +
@@ -1,396 +0,0 @@
-# Copyright (C) 2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-In-memory implementations of Mailman interfaces, for testing purposes.
-
-import datetime
-import urlparse
-
-from Mailman import Utils
-from Mailman import passwords
-from Mailman.interfaces import *
-
-from zope.interface import implements
-
-
-
-class UserManager(object):
-implements(IUserManager)
-
-def __init__(self):
-self._users = set()
-self._next_id   = 1
-
-@property
-def users(self):
-for user in self._users:
-yield user
-
-def create_user(self):
-user = User(self._next_id, self)
-self._next_id += 1
-self._users.add(user)
-return user
-
-def remove(self, user):
-self._users.discard(user)
-
-def get(self, address):
-# Yes, this is slow and icky, but it's only for testing purposes
-for user in self._users:
-if user.controls(address):
-return user
-return None
-
-
-
-class User(object):
-implements(IUser)
-
-def __init__(self, user_id, user_mgr):
-self._user_id   = user_id
-self._user_mgr  = user_mgr
-self._addresses = set()
-self.real_name  = u''
-self.password   = passwords.NoPasswordScheme.make_secret('ignore')
-self.default_profile = None
-
-def __eq__(self, other):
-return (IUser.implementedBy(other) and
-self.user_id == other.user_id and
-self.user_manager is other.user_manager)
-
-def __ne__(self, other):
-return not self.__eq__(other)
-
-@property
-def user_id(self):
-return self._user_id
-
-@property
-def user_manager(self):
-return self._user_mgr
-
-@property
-def addresses(self):
-for address in self._addresses:
-yield address
-
-def add_address(self, address):
-if self.controls(address):
-return
-user_address = Address(address, self)
-self._addresses.add(user_address)
-
-def remove_address(self, address):
-if not self.controls(address):
-return
-user_address = Address(address, self)
-self._addresses.discard(user_address)
-
-def controls(self, address):
-for user_address in self.addresses:
-if user_address == address:
-return True
-return False
-
-
-
-class Address(object):
-implements(IAddress)
-
-def __init__(self, email_address, user, profile=None):
-self._address   = email_address
-self._user  = user
-self.profile= profile or Profile()
-self.validated_on   = None
-
-def __eq__(self, other):
-return (IAddress.implementedBy(other) and
-self.address == other.address and
-self.user == other.user)
-
-@property
-def address(self):
-return self._address
-
-@property
-def user(self):
-return self._user
-
-
-
-class OkayToPost(object):
-implements(IPostingPermission)
-
-# XXX
-okay_to_post = True
-
-
-
-class Roster(object):
-implements(IRoster)
-
-def __init__(self, name):
-self._name  = name
-self._members   = set()
-
-def __eq__(self, other):
-return (IRoster.implementedBy(other) and
-self.name == other.name)
-
-def 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-08 Thread noreply

revno: 6526
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-07-08 07:47:03 -0400
message:
  Convert ToOutgoing handler to a doctest.  Minor cleanup of the handler code to
  a more modern Python style.
added:
  Mailman/docs/outgoing.txt
modified:
  Mailman/Handlers/ToOutgoing.py
  Mailman/testing/test_handlers.py

=== added file 'Mailman/docs/outgoing.txt'
--- a/Mailman/docs/outgoing.txt 1970-01-01 00:00:00 +
+++ b/Mailman/docs/outgoing.txt 2007-07-08 11:47:03 +
@@ -0,0 +1,150 @@
+The outgoing handler
+
+
+Mailman's outgoing queue is used as the wrapper around SMTP delivery to the
+upstream mail server.  The ToOutgoing handler does little more than drop the
+message into the outgoing queue, after calculating whether the message should
+be VERP'd or not.  VERP means Variable Envelope Return Path; we're using that
+term somewhat incorrectly, but within the spirit of the standard, which
+basically describes how to encode the recipient's address in the originator
+headers for unambigous bounce processing.
+
+ from Mailman.Handlers.ToOutgoing import process
+ from Mailman.Message import Message
+ from Mailman.Queue.Switchboard import Switchboard
+ from Mailman.configuration import config
+ from Mailman.database import flush
+ from email import message_from_string
+ mlist = config.list_manager.create('[EMAIL PROTECTED]')
+ flush()
+ switchboard = Switchboard(config.OUTQUEUE_DIR)
+
+ def queue_size():
+... size = len(switchboard.files)
+... for filebase in switchboard.files:
+... msg, msgdata = switchboard.dequeue(filebase)
+... switchboard.finish(filebase)
+... return size
+
+Craft a message destined for the outgoing queue.  Include some random metadata
+as if this message had passed through some other handlers.
+
+ msg = message_from_string(\
+... Subject: Here is a message
+...
+... Something of great import.
+... , Message)
+
+When certain conditions are met, the message will be VERP'd.  For example, if
+the message metadata already has a VERP key, this message will be VERP'd.
+
+ msgdata = dict(foo=1, bar=2, verp=True)
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+Subject: Here is a message
+BLANKLINE
+Something of great import.
+ msgdata['verp']
+True
+
+While the queued message will not be changed, the queued metadata will have an
+additional key set: the mailing list name.
+
+ filebase = switchboard.files[0]
+ qmsg, qmsgdata = switchboard.dequeue(filebase)
+ switchboard.finish(filebase)
+ print qmsg.as_string()
+Subject: Here is a message
+BLANKLINE
+Something of great import.
+ sorted(qmsgdata.items())
+[('_parsemsg', False),
+ ('bar', 2), ('foo', 1),
+ ('listname', '[EMAIL PROTECTED]'),
+ ('received_time', ...),
+ ('verp', True), ('version', 3)]
+ queue_size()
+0
+
+If the list is set to personalize deliveries, and the global configuration
+option to VERP personalized deliveries is set, then the message will be
+VERP'd.
+
+ mlist.personalize = True
+ flush()
+ config.VERP_PERSONALIZED_DELIVERIES = True
+ msgdata = dict(foo=1, bar=2)
+ process(mlist, msg, msgdata)
+ msgdata['verp']
+True
+ queue_size()
+1
+
+However, if the global configuration variable prohibits VERP'ing, even
+personalized lists will not VERP.
+
+ config.VERP_PERSONALIZED_DELIVERIES = False
+ msgdata = dict(foo=1, bar=2)
+ process(mlist, msg, msgdata)
+ print msgdata.get('verp')
+None
+ queue_size()
+1
+
+If the list is not personalized, then the message may still be VERP'd based on
+the global configuration variable VERP_DELIVERY_INTERVAL.  This variable tells
+Mailman how often to VERP even non-personalized mailing lists.  It can be set
+to zero, which means non-personalized messages will never be VERP'd.
+
+ mlist.personalize = False
+ flush()
+ config.VERP_DELIVERY_INTERVAL = 0
+ msgdata = dict(foo=1, bar=2)
+ process(mlist, msg, msgdata)
+ print msgdata.get('verp')
+None
+ queue_size()
+1
+
+If the interval is set to 1, then every message will be VERP'd.
+
+ config.VERP_DELIVERY_INTERVAL = 1
+ for i in range(10):
+... msgdata = dict(foo=1, bar=2)
+... process(mlist, msg, msgdata)
+... print i, msgdata['verp']
+0 True
+1 True
+2 True
+3 True
+4 True
+5 True
+6 True
+7 True
+8 True
+9 True
+ queue_size()
+10
+
+If the interval is set to some other number, then one out of that many posts
+will be VERP'd.
+
+ config.VERP_DELIVERY_INTERVAL = 3
+ for i in range(10):
+... mlist.post_id = i
+... flush()
+... msgdata = dict(foo=1, bar=2)
+... process(mlist, msg, msgdata)
+... print i, 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-05 Thread noreply

revno: 6523
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Thu 2007-07-05 10:29:40 -0400
message:
  Convert the SpamDetect handler tests (what there was of them anyway) to a
  doctest, but don't otherwise clean up the handler module.
added:
  Mailman/docs/antispam.txt
modified:
  Mailman/testing/test_handlers.py

=== added file 'Mailman/docs/antispam.txt'
--- a/Mailman/docs/antispam.txt 1970-01-01 00:00:00 +
+++ b/Mailman/docs/antispam.txt 2007-07-05 14:29:40 +
@@ -0,0 +1,75 @@
+Anti-spam defences
+==
+
+By design, Mailman does not have very sophisticated anti-spam measures because
+this type of filtering is done much more efficiently at the MTA level.  For
+example, if Mailman were to do spam detection, it could not reject the message
+at SMTP time.
+
+Still, Mailman does employ a small number of rather ham-handed anti-spam
+measures.
+
+ from Mailman.Handlers.SpamDetect import process
+ from Mailman.Message import Message
+ from Mailman.Queue.Switchboard import Switchboard
+ from Mailman.configuration import config
+ from Mailman.database import flush
+ from email import message_from_string
+ mlist = config.list_manager.create('[EMAIL PROTECTED]')
+ flush()
+
+
+Short circuiting
+
+
+If a message is pre-approved, this handler does nothing.
+
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+...
+... An important message.
+... , Message)
+ msgdata = {'approved': True}
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+From: [EMAIL PROTECTED]
+BLANKLINE
+An important message.
+BLANKLINE
+ msgdata
+{'approved': True}
+
+
+Header matching
+---
+
+There is a global configuration variable that can be set to a list of header
+matches.  Each item in that list is a 2-tuple of the header to match and a
+regular expression.  For example, if we wanted to block all message that come
+from 'aperson' regardless of the domain, we'd do something like the following
+in our mailman.cfg file:
+
+ config.KNOWN_SPAMMERS.append(('from', 'aperson'))
+
+Now if the same message is posted to the mailing list, and that message is not
+pre-approved.  The handler will throw an exception that signals the message is
+spam.
+
+ msgdata = {}
+ process(mlist, msg, msgdata)
+Traceback (most recent call last):
+...
+SpamDetected
+ print msg.as_string()
+From: [EMAIL PROTECTED]
+BLANKLINE
+An important message.
+BLANKLINE
+ msgdata
+{}
+
+
+Header filter rules
+---
+
+XXX Need tests.

=== modified file 'Mailman/testing/test_handlers.py'
--- a/Mailman/testing/test_handlers.py  2007-07-04 04:16:48 +
+++ b/Mailman/testing/test_handlers.py  2007-07-05 14:29:40 +
@@ -43,7 +43,6 @@
 from Mailman.Handlers import Moderate
 from Mailman.Handlers import Scrubber
 # Don't test handlers such as SMTPDirect and Sendmail here
-from Mailman.Handlers import SpamDetect
 from Mailman.Handlers import ToArchive
 from Mailman.Handlers import ToDigest
 from Mailman.Handlers import ToOutgoing
@@ -306,11 +305,6 @@
 
 
 
-class TestModerate(TestBase):
-pass
-
-
-
 class TestScrubber(TestBase):
 def test_save_attachment(self):
 mlist = self._mlist
@@ -403,36 +397,6 @@
 
 
 
-class TestSpamDetect(TestBase):
-def test_short_circuit(self):
-msgdata = {'approved': 1}
-rtn = SpamDetect.process(self._mlist, None, msgdata)
-# Not really a great test, but there's little else to assert
-self.assertEqual(rtn, None)
-
-def test_spam_detect(self):
-msg1 = email.message_from_string(\
-From: [EMAIL PROTECTED]
-
-A message.
-)
-msg2 = email.message_from_string(\
-To: [EMAIL PROTECTED]
-
-A message.
-)
-spammers = config.KNOWN_SPAMMERS[:]
-try:
-config.KNOWN_SPAMMERS.append(('from', '.?person'))
-self.assertRaises(SpamDetect.SpamDetected,
-  SpamDetect.process, self._mlist, msg1, {})
-rtn = SpamDetect.process(self._mlist, msg2, {})
-self.assertEqual(rtn, None)
-finally:
-config.KNOWN_SPAMMERS = spammers
-
-
-
 class TestToArchive(TestBase):
 def setUp(self):
 TestBase.setUp(self)
@@ -694,9 +658,7 @@
 suite = unittest.TestSuite()
 suite.addTest(unittest.makeSuite(TestApprove))
 suite.addTest(unittest.makeSuite(TestMimeDel))
-suite.addTest(unittest.makeSuite(TestModerate))
 suite.addTest(unittest.makeSuite(TestScrubber))
-suite.addTest(unittest.makeSuite(TestSpamDetect))
 suite.addTest(unittest.makeSuite(TestToArchive))
 suite.addTest(unittest.makeSuite(TestToDigest))
 suite.addTest(unittest.makeSuite(TestToOutgoing))



--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-03 Thread noreply

revno: 6522
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Wed 2007-07-04 00:16:48 -0400
message:
  Convert the TestTagger to a doctest.  No other cleaning up of the handler
  module is done.
added:
  Mailman/docs/tagger.txt
modified:
  Mailman/testing/test_handlers.py

=== added file 'Mailman/docs/tagger.txt'
--- a/Mailman/docs/tagger.txt   1970-01-01 00:00:00 +
+++ b/Mailman/docs/tagger.txt   2007-07-04 04:16:48 +
@@ -0,0 +1,244 @@
+Message tagger
+==
+
+Mailman has a topics system which works like this: a mailing list
+administrator sets up one or more topics, which is essentially a named regular
+expression.  The topic name can be any arbitrary string, and the name serves
+double duty as the 'topic tag'.  Each message that flows the mailing list has
+its Subject: and Keywords: headers compared against these regular
+expressions.  The message then gets tagged with the topic names of each hit.
+
+ from Mailman.Handlers.Tagger import process
+ from Mailman.Message import Message
+ from Mailman.Queue.Switchboard import Switchboard
+ from Mailman.configuration import config
+ from Mailman.database import flush
+ from email import message_from_string
+ mlist = config.list_manager.create('[EMAIL PROTECTED]')
+
+Topics must be enabled for Mailman to do any topic matching, even if topics
+are defined.
+
+ mlist.topics = [('bar fight', '.*bar.*', 'catch any bars', False)]
+ mlist.topics_enabled = False
+ mlist.topics_bodylines_limit = 0
+ flush()
+
+ msg = message_from_string(\
+... Subject: foobar
+... Keywords: barbaz
+...
+... , Message)
+ msgdata = {}
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+Subject: foobar
+Keywords: barbaz
+BLANKLINE
+BLANKLINE
+ msgdata
+{}
+
+However, once topics are enabled, message will be tagged.  There are two
+artifacts of tagging; an X-Topics: header is added with the topic name, and
+the message metadata gets a key with a list of matching topic names.
+
+ mlist.topics_enabled = True
+ flush()
+ msg = message_from_string(\
+... Subject: foobar
+... Keywords: barbaz
+...
+... , Message)
+ msgdata = {}
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+Subject: foobar
+Keywords: barbaz
+X-Topics: bar fight
+BLANKLINE
+BLANKLINE
+ msgdata['topichits']
+['bar fight']
+
+
+Scanning body lines
+---
+
+The tagger can also look at a certain number of body lines, but only for
+Subject: and Keyword: header-like lines.  When set to zero, no body lines are
+scanned.
+
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... Subject: nothing
+... Keywords: at all
+...
+... X-Ignore: something else
+... Subject: foobar
+... Keywords: barbaz
+... , Message)
+ msgdata = {}
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+From: [EMAIL PROTECTED]
+Subject: nothing
+Keywords: at all
+BLANKLINE
+X-Ignore: something else
+Subject: foobar
+Keywords: barbaz
+BLANKLINE
+ msgdata
+{}
+
+But let the tagger scan a few body lines and the matching headers will be
+found.
+
+ mlist.topics_bodylines_limit = 5
+ flush()
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... Subject: nothing
+... Keywords: at all
+...
+... X-Ignore: something else
+... Subject: foobar
+... Keywords: barbaz
+... , Message)
+ msgdata = {}
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+From: [EMAIL PROTECTED]
+Subject: nothing
+Keywords: at all
+X-Topics: bar fight
+BLANKLINE
+X-Ignore: something else
+Subject: foobar
+Keywords: barbaz
+BLANKLINE
+ msgdata['topichits']
+['bar fight']
+
+However, scanning stops at the first body line that doesn't look like a
+header.
+
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... Subject: nothing
+... Keywords: at all
+...
+... This is not a header
+... Subject: foobar
+... Keywords: barbaz
+... , Message)
+ msgdata = {}
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+From: [EMAIL PROTECTED]
+Subject: nothing
+Keywords: at all
+BLANKLINE
+This is not a header
+Subject: foobar
+Keywords: barbaz
+ msgdata
+{}
+
+When set to a negative number, all body lines will be scanned.
+
+ mlist.topics_bodylines_limit = -1
+ flush()
+ lots_of_headers = '\n'.join(['X-Ignore: zip'] * 100)
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... Subject: nothing
+... Keywords: at all
+...
+... %s
+... Subject: foobar
+... Keywords: barbaz
+...  % lots_of_headers, Message)
+ msgdata = {}
+ process(mlist, msg, msgdata)
+ # 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-02 Thread noreply

revno: 6521
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Tue 2007-07-03 01:09:53 -0400
message:
  Convert TestHold to a doctest and update code as necessary.  No general
  modernization of Hold.py was performed.
added:
  Mailman/docs/hold.txt
modified:
  Mailman/Handlers/Hold.py
  Mailman/ListAdmin.py
  Mailman/MailList.py
  Mailman/database/model/mailinglist.py
  Mailman/testing/test_handlers.py

=== added file 'Mailman/docs/hold.txt'
--- a/Mailman/docs/hold.txt 1970-01-01 00:00:00 +
+++ b/Mailman/docs/hold.txt 2007-07-03 05:09:53 +
@@ -0,0 +1,373 @@
+Holding messages
+
+
+One of the most important functions of Mailman is to moderate messages by
+holding some for approval before they will post to the mailing list.  Messages
+are held when they meet any of a number of criteria.
+
+ import os
+ import errno
+ from Mailman.Handlers.Hold import process
+ from Mailman.Queue.Switchboard import Switchboard
+ from Mailman.configuration import config
+ from Mailman.database import flush
+ mlist = config.list_manager.create('[EMAIL PROTECTED]')
+ mlist.preferred_language = 'en'
+ mlist.real_name = '_XTest'
+ # XXX This will almost certainly change once we've worked out the web
+ # space layout for mailing lists now.
+ mlist._data.web_page_url = 'http://lists.example.com/'
+ flush()
+
+XXX The Hold handler requires that the mailing list be locked because it
+touches the pending database.  Eventually the pending database should be moved
+into the real database so that the lock is no longer necessary.
+
+ mlist.Lock()
+
+Here's a helper function used when we don't care about what's in the virgin
+queue or in the pending database.
+
+ switchboard = Switchboard(config.VIRGINQUEUE_DIR)
+ def clear():
+... for filebase in switchboard.files:
+... msg, msgdata = switchboard.dequeue(filebase)
+... switchboard.finish(filebase)
+... for holdfile in os.listdir(config.DATA_DIR):
+... if holdfile.startswith('heldmsg-'):
+... os.unlink(os.path.join(config.DATA_DIR, holdfile))
+... try:
+... os.unlink(os.path.join(config.DATA_DIR, 'pending.db'))
+... except OSError, e:
+... if e.errno  errno.ENOENT:
+... raise
+
+
+Short circuiting
+
+
+If the message metadata indicates that the message is pre-approved, then the
+handler returns immediately.
+
+ from email import message_from_string
+ from Mailman.Message import Message
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+...
+... An important message.
+... , Message)
+ msgdata = {'approved': True}
+ process(mlist, msg, msgdata)
+ print msg.as_string()
+From: [EMAIL PROTECTED]
+BLANKLINE
+An important message.
+BLANKLINE
+ msgdata
+{'approved': True}
+
+
+Administrivia
+-
+
+Mailman scans parts of the message for administrivia, meaning text that looks
+like an email command.  This is to prevent people sending 'help' or
+'subscribe' message, etc. to the list members.  First, we enable the scanning
+of administrivia for the list.
+
+ mlist.administrivia = True
+ flush()
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... Subject: unsubscribe
+...
+... , Message)
+ process(mlist, msg, {})
+Traceback (most recent call last):
+...
+Administrivia
+ clear()
+
+
+Maximum number of recipients
+
+
+Mailman will hold messages that have more than a specified number of explicit
+recipients.
+
+ mlist.max_num_recipients = 5
+ flush()
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... To: [EMAIL PROTECTED], [EMAIL PROTECTED]
+... Cc: [EMAIL PROTECTED]
+... Cc: [EMAIL PROTECTED] (Dan Person)
+... To: Elly Q. Person [EMAIL PROTECTED]
+...
+... Hey folks!
+... , Message)
+ process(mlist, msg, {})
+Traceback (most recent call last):
+...
+TooManyRecipients
+ clear()
+
+
+Implicit destination
+
+
+Mailman will hold messages that have implicit destination, meaning that the
+mailing list's posting address isn't included in the explicit recipients.
+
+ mlist.require_explicit_destination = True
+ mlist.acceptable_aliases = ''
+ flush()
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... Subject: An implicit message
+...
+... , Message)
+ process(mlist, msg, {})
+Traceback (most recent call last):
+...
+ImplicitDestination
+ clear()
+
+A message gated from NNTP will obviously have an implicit destination.  Such
+gated messages will not be held for implicit destination because it's assumed
+that Mailman pulled it from the appropriate news group.
+
+ msgdata = 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-01 Thread noreply

revno: 6517
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-07-01 11:51:09 -0400
message:
  Support for case-preserving addresses.  When an Address is given an email
  address that is not lower cased, the original, case-preserved version is store
  on the '_original' attribute.  The lower-cased version is always used as the
  key and thus always stored on the 'address' attribute.  The IAddress interface
  is given a new 'original_address' property which returns the case-preserved
  version.
  
  Address's __str__() and __repr__() are similarly modified.  The former always
  includes the case-preserved address; the latter does too, but now also
  includes the lower-cased 'key' email address (along with the object's id).
  
  Searching for an address always does so on the lower-cased version.
  
  Test suite is updated as necessary.  Also, I'm adding the
  REPORT_ONLY_FIRST_FAILURE doctest flag so that it's easier to debug doctest
  failures without having pages of problems to scroll through.
modified:
  Mailman/database/model/address.py
  Mailman/database/usermanager.py
  Mailman/docs/addresses.txt
  Mailman/docs/users.txt
  Mailman/interfaces/address.py
  Mailman/testing/test_documentation.py

=== modified file 'Mailman/database/model/address.py'
--- a/Mailman/database/model/address.py 2007-06-16 02:37:33 +
+++ b/Mailman/database/model/address.py 2007-07-01 15:51:09 +
@@ -31,6 +31,7 @@
 implements(IAddress)
 
 has_field('address',Unicode)
+has_field('_original',  Unicode)
 has_field('real_name',  Unicode)
 has_field('verified',   Boolean)
 has_field('registered_on',  DateTime)
@@ -41,12 +42,26 @@
 # Options
 using_options(shortnames=True)
 
+def __init__(self, address, real_name):
+super(Address, self).__init__()
+lower_case = address.lower()
+self.address = lower_case
+self.real_name = real_name
+self._original = (None if lower_case == address else address)
+
 def __str__(self):
-return formataddr((self.real_name, self.address))
+addr = (self.address if self._original is None else self._original)
+return formataddr((self.real_name, addr))
 
 def __repr__(self):
-return 'Address: %s [%s]' % (
-str(self), ('verified' if self.verified else 'not verified'))
+verified = ('verified' if self.verified else 'not verified')
+address_str = str(self)
+if self._original is None:
+return 'Address: %s [%s] at %#x' % (
+address_str, verified, id(self))
+else:
+return 'Address: %s [%s] key: %s at %#x' % (
+address_str, verified, self.address, id(self))
 
 def subscribe(self, mlist, role):
 from Mailman.database.model import Member
@@ -57,3 +72,7 @@
 address=self)
 member.preferences = Preferences()
 return member
+
+@property
+def original_address(self):
+return (self.address if self._original is None else self._original)

=== modified file 'Mailman/database/usermanager.py'
--- a/Mailman/database/usermanager.py   2007-06-16 02:37:33 +
+++ b/Mailman/database/usermanager.py   2007-07-01 15:51:09 +
@@ -39,7 +39,7 @@
 user = User()
 user.real_name = (real_name if real_name is not None else '')
 if address:
-addrobj = Address(address=address, real_name=user.real_name)
+addrobj = Address(address, user.real_name)
 addrobj.preferences = Preferences()
 user.link(addrobj)
 user.preferences = Preferences()
@@ -54,16 +54,16 @@
 yield user
 
 def get_user(self, address):
-found = Address.get_by(address=address)
+found = Address.get_by(address=address.lower())
 return found and found.user
 
 def create_address(self, address, real_name=None):
-found = Address.get_by(address=address)
+found = Address.get_by(address=address.lower())
 if found:
-raise Errors.ExistingAddressError(address)
+raise Errors.ExistingAddressError(found.original_address)
 if real_name is None:
 real_name = ''
-address = Address(address=address, real_name=real_name)
+address = Address(address, real_name)
 address.preferences = Preferences()
 return address
 
@@ -75,7 +75,7 @@
 address.delete()
 
 def get_address(self, address):
-return Address.get_by(address=address)
+return Address.get_by(address=address.lower())
 
 @property
 def addresses(self):

=== modified file 'Mailman/docs/addresses.txt'
--- a/Mailman/docs/addresses.txt2007-06-22 21:15:03 +
+++ b/Mailman/docs/addresses.txt2007-07-01 15:51:09 +
@@ -41,6 +41,14 @@
  sorted(address.real_name for address in mgr.addresses)
   

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-01 Thread noreply

revno: 6518
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-07-01 21:49:34 -0400
message:
  Add support for code coverage with 'testall --coverage'.  However, I'm not
  convinced this is totally accurate as a full test run shows almost no coverage
  in the Mailman.database.model modules even though I /know/ they're getting
  executed.
  
  I'll need to figure this out, but eventually we'll convert fully to setuptools
  and then we'll use the nosetests to do testing and coverage.
added:
  misc/coverage.py
modified:
  Mailman/bin/testall.py
  misc/Makefile.in

=== added file 'misc/coverage.py'
--- a/misc/coverage.py  1970-01-01 00:00:00 +
+++ b/misc/coverage.py  2007-07-02 01:49:34 +
@@ -0,0 +1,952 @@
+#!/usr/bin/python
+#
+# Perforce Defect Tracking Integration Project
+#  http://www.ravenbrook.com/project/p4dti/
+#
+#   COVERAGE.PY -- COVERAGE TESTING
+#
+# Gareth Rees, Ravenbrook Limited, 2001-12-04
+# Ned Batchelder, 2004-12-12
+# http://nedbatchelder.com/code/modules/coverage.html
+#
+#
+# 1. INTRODUCTION
+#
+# This module provides coverage testing for Python code.
+#
+# The intended readership is all Python developers.
+#
+# This document is not confidential.
+#
+# See [GDR 2001-12-04a] for the command-line interface, programmatic
+# interface and limitations.  See [GDR 2001-12-04b] for requirements and
+# design.
+
+rUsage:
+
+coverage.py -x [-p] MODULE.py [ARG1 ARG2 ...]
+Execute module, passing the given command-line arguments, collecting
+coverage data. With the -p option, write to a temporary file containing
+the machine name and process ID.
+
+coverage.py -e
+Erase collected coverage data.
+
+coverage.py -c
+Collect data from multiple coverage files (as created by -p option above)
+and store it into a single file representing the union of the coverage.
+
+coverage.py -r [-m] [-o dir1,dir2,...] FILE1 FILE2 ...
+Report on the statement coverage for the given files.  With the -m
+option, show line numbers of the statements that weren't executed.
+
+coverage.py -a [-d dir] [-o dir1,dir2,...] FILE1 FILE2 ...
+Make annotated copies of the given files, marking statements that
+are executed with  and statements that are missed with !.  With
+the -d option, make the copies in that directory.  Without the -d
+option, make each copy in the same directory as the original.
+
+-o dir,dir2,...
+  Omit reporting or annotating files when their filename path starts with
+  a directory listed in the omit list.
+  e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits
+
+Coverage data is saved in the file .coverage by default.  Set the
+COVERAGE_FILE environment variable to save it somewhere else.
+
+__version__ = 2.6.20060823# see detailed history at the end of this file.
+
+import compiler
+import compiler.visitor
+import os
+import re
+import string
+import sys
+import threading
+import types
+from socket import gethostname
+
+# 2. IMPLEMENTATION
+#
+# This uses the singleton pattern.
+#
+# The word morf means a module object (from which the source file can
+# be deduced by suitable manipulation of the __file__ attribute) or a
+# filename.
+#
+# When we generate a coverage report we have to canonicalize every
+# filename in the coverage dictionary just in case it refers to the
+# module we are reporting on.  It seems a shame to throw away this
+# information so the data in the coverage dictionary is transferred to
+# the 'cexecuted' dictionary under the canonical filenames.
+#
+# The coverage dictionary is called c and the trace function t.  The
+# reason for these short names is that Python looks up variables by name
+# at runtime and so execution time depends on the length of variables!
+# In the bottleneck of this application it's appropriate to abbreviate
+# names to increase speed.
+
+class StatementFindingAstVisitor(compiler.visitor.ASTVisitor):
+def __init__(self, statements, excluded, suite_spots):
+compiler.visitor.ASTVisitor.__init__(self)
+self.statements = statements
+self.excluded = excluded
+self.suite_spots = suite_spots
+self.excluding_suite = 0
+
+def doRecursive(self, node):
+self.recordNodeLine(node)
+for n in node.getChildNodes():
+self.dispatch(n)
+
+visitStmt = visitModule = doRecursive
+
+def doCode(self, node):
+if hasattr(node, 'decorators') and node.decorators:
+self.dispatch(node.decorators)
+self.recordAndDispatch(node.code)
+else:
+self.doSuite(node, node.code)
+
+visitFunction = visitClass = doCode
+
+def getFirstLine(self, node):
+# Find the first line in the tree node.
+lineno = node.lineno
+for n in node.getChildNodes():
+f = 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-07-01 Thread noreply

revno: 6519
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sun 2007-07-01 23:31:21 -0400
message:
  Convert failing test_message.py to doctests bounces.txt and message.txt, which
  of course now succeed.
  
  Rename Bouncer.py's BounceMessage() method to bounce_message() and remove the
  'msgdata' parameter, which wasn't being used.  Change the RejectNotice
  exception class to expose .notice directly, as there's no reason for this to
  be an accessor or property.
  
  Move the coverage.py installation to the install-packages target instead of
  the install-other target, so that it only gets installed once the pythonlib
  directory is created.
removed:
  Mailman/testing/test_message.py
added:
  Mailman/docs/bounces.txt
  Mailman/docs/message.txt
modified:
  Mailman/Bouncer.py
  Mailman/Errors.py
  Mailman/Queue/IncomingRunner.py
  misc/Makefile.in

=== removed file 'Mailman/testing/test_message.py'
--- a/Mailman/testing/test_message.py   2007-01-19 04:38:06 +
+++ b/Mailman/testing/test_message.py   1970-01-01 00:00:00 +
@@ -1,98 +0,0 @@
-# Copyright (C) 2001-2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-Unit tests for the various Message class methods.
-
-import email
-import unittest
-
-from Mailman import Errors
-from Mailman import Message
-from Mailman import Version
-from Mailman.testing.emailbase import EmailBase
-
-
-
-class TestSentMessage(EmailBase):
-def test_user_notification(self):
-eq = self.assertEqual
-unless = self.failUnless
-msg = Message.UserNotification(
-'[EMAIL PROTECTED]',
-'[EMAIL PROTECTED]',
-'Your Test List',
-'About your test list')
-msg.send(self._mlist)
-qmsg = email.message_from_string(self._readmsg())
-eq(qmsg['subject'], 'Your Test List')
-eq(qmsg['from'], '[EMAIL PROTECTED]')
-eq(qmsg['to'], '[EMAIL PROTECTED]')
-# The Message-ID: header has some time-variant information
-msgid = qmsg['message-id']
-unless(msgid.startswith('mailman.'))
-unless(msgid.endswith('[EMAIL PROTECTED]'))
-eq(qmsg['sender'], '[EMAIL PROTECTED]')
-eq(qmsg['errors-to'], '[EMAIL PROTECTED]')
-eq(qmsg['x-beenthere'], '[EMAIL PROTECTED]')
-eq(qmsg['x-mailman-version'], Version.VERSION)
-eq(qmsg['precedence'], 'bulk')
-# UserNotifications have reduced_list_headers so it won't have
-# List-Help, List-Subscribe, or List-Unsubscribe.  XXX Why would that
-# possibly be?
-eq(qmsg['list-help'],
-   'mailto:[EMAIL PROTECTED]')
-eq(qmsg['list-subscribe'], \
-http://www.example.com/mailman/listinfo/[EMAIL PROTECTED], 
-\tmailto:[EMAIL PROTECTED])
-eq(qmsg['list-id'], '_xtest.example.com')
-eq(qmsg['list-unsubscribe'], \
-http://www.example.com/mailman/listinfo/[EMAIL PROTECTED], 
-\tmailto:[EMAIL PROTECTED])
-eq(qmsg.get_payload(), 'About your test list')
-
-def test_bounce_message(self):
-eq = self.assertEqual
-unless = self.failUnless
-msg = email.message_from_string(\
-To: [EMAIL PROTECTED]
-From: [EMAIL PROTECTED]
-Subject: and another thing
-
-yadda yadda yadda
-, Message.Message)
-self._mlist.BounceMessage(msg, {})
-qmsg = email.message_from_string(self._readmsg())
-unless(qmsg.is_multipart())
-eq(len(qmsg.get_payload()), 2)
-# The first payload is the details of the bounce action, and the
-# second message is the message/rfc822 attachment of the original
-# message.
-msg1 = qmsg.get_payload(0)
-eq(msg1.get_content_type(), 'text/plain')
-eq(msg1.get_payload(), '[No bounce details are available]')
-msg2 = qmsg.get_payload(1)
-eq(msg2.get_content_type(), 'message/rfc822')
-unless(msg2.is_multipart())
-msg3 = msg2.get_payload(0)
-eq(msg3.get_payload(), 'yadda yadda yadda\n')
-
-
-
-def test_suite():
-suite = unittest.TestSuite()
-suite.addTest(unittest.makeSuite(TestSentMessage))
-return suite

=== added file 'Mailman/docs/bounces.txt'
--- a/Mailman/docs/bounces.txt  1970-01-01 00:00:00 +

Re: [Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-28 Thread Barry Warsaw
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

Hi Mark, thanks for fixing this.  Just a couple of quick comments...

On Jun 28, 2007, at 1:27 PM, [EMAIL PROTECTED] wrote:

 @@ -100,20 +101,25 @@
  os.chown(path, -1, MAILMAN_GID)
  else:
  print
 -# All directories must be at least rwxrwsr-x.  Don't check  
 the private
 -# archive directory or database directory themselves since  
 these are
 -# checked in checkarchives() and checkarchivedbs() below.
 +# Most directories must be at least rwxrwsr-x.
 +# The private archive directory  and database directory  
 must be at
 +# least rwxrws---.  Their 'other' permissions are checked in
 +# checkarchives() and checkarchivedbs() below.  Their  
 'user' and
 +# 'group' permissions are checked here.
 +# The directories under qfiles should be rwxrws---.  Their  
 'user' and
 +# 'group' permissions are checked here.  Their 'other'  
 permissions
 +# aren't checked.
  private = config.PRIVATE_ARCHIVE_FILE_DIR
 -if path == private or (os.path.commonprefix((path,  
 private)) == private
 -   and os.path.split(path)[1] ==  
 'database'):
 -continue
 -# The directories under qfiles should have a more limited  
 permission
 -if os.path.commonprefix((path, config.QUEUE_DIR)) ==  
 config.QUEUE_DIR:
 +if path == private or \
 +  (os.path.commonprefix((path, private)) == private
 +   and os.path.split(path)[1] == 'database'):

This is probably better style:

 if path == private or (
 os.path.commonprefix((path, private)) == private
 and os.path.split(path)[1] == 'database'):
 # then...
 targetperms = PRIVATEPERMS

It eliminates a backslash (always ugly ;) though it kind of begs for  
the 'then...' comment because of the way the columns line up.

 +targetperms = PRIVATEPERMS
 +elif os.path.commonprefix((path, config.QUEUE_DIR)) \
 +  == config.QUEUE_DIR:
  targetperms = QFILEPERMS

Similarly, this removes the need for a backslash:

 elif (os.path.commonprefix((path, config.QUEUE_DIR))
   == config.QUEUE_DIR):
 targetperms = QFILEPERMS

You probably can't do much better without storing config.QUEUE_DIR in  
a local variable.

Cheers,
- -Barry



-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.7 (Darwin)

iQCVAwUBRoP6mHEjvBPtnXfVAQL8qQP9GZzd1T5xdGGmcWvGR+lpZPUpZPGWJhCq
7yH54o8E45pBisH6LVVJR3KoS7/xfJGevj3t/tQ6gLgiOsdeiLaTWQJ05UBBiHAr
r8+7FWWgUrc84o7EvkhgXXsqCVT/iFTW1yVWmlsVGLm0ezdl6oOPTcrpJ2biA63S
3Hn//tQKi2o=
=rA0r
-END PGP SIGNATURE-
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-28 Thread noreply

revno: 6516
committer: Mark Sapiro [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Thu 2007-06-28 12:23:25 -0700
message:
  Cleaned up a couple of style issues.  No actual code changes.
modified:
  Mailman/bin/check_perms.py

=== modified file 'Mailman/bin/check_perms.py'
--- a/Mailman/bin/check_perms.py2007-06-28 17:23:27 +
+++ b/Mailman/bin/check_perms.py2007-06-28 19:23:25 +
@@ -110,12 +110,13 @@
 # 'group' permissions are checked here.  Their 'other' permissions
 # aren't checked.
 private = config.PRIVATE_ARCHIVE_FILE_DIR
-if path == private or \
-  (os.path.commonprefix((path, private)) == private
-   and os.path.split(path)[1] == 'database'):
+if path == private or (
+os.path.commonprefix((path, private)) == private
+and os.path.split(path)[1] == 'database'):
+# then...
 targetperms = PRIVATEPERMS
-elif os.path.commonprefix((path, config.QUEUE_DIR)) \
-  == config.QUEUE_DIR:
+elif (os.path.commonprefix((path, config.QUEUE_DIR))
+  == config.QUEUE_DIR):
 targetperms = QFILEPERMS
 else:
 targetperms = DIRPERMS
@@ -402,7 +403,7 @@
 else:
 print _('Problems found:'), STATE.ERRORS
 print _('Re-run as $MAILMAN_USER (or root) with -f flag to fix')
-
+
 
 if __name__ == '__main__':
 main()



--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-27 Thread noreply

revno: 6512
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Wed 2007-06-27 17:33:42 -0400
message:
  Convert the Switchboard test in test_runners.py to a doctest.  Add an
  ISwitchboard interface and modernize the Python code in the Switchboard.py
  implementation.
  
  The SAVE_MSGS_AS_PICKLES option is removed.  Messages are always saved as
  pickles unless the metadata '_plaintext' key is present, though this should
  eventually go away too.
  
  In testall.py, put the entire VAR_PREFIX in a temporary directory.  This helps
  the switchboard tests by not mixing their data with the installation's queue
  directories.  The Configuration object now also ensures that all the queue and
  log directories exist -- one more step on the road to getting rid of the
  autoconf mess.
added:
  Mailman/docs/switchboard.txt
  Mailman/interfaces/switchboard.py
modified:
  Mailman/Queue/Runner.py
  Mailman/Queue/Switchboard.py
  Mailman/Queue/tests/test_runners.py
  Mailman/bin/testall.py
  Mailman/configuration.py
  Mailman/initialize.py

=== added file 'Mailman/docs/switchboard.txt'
--- a/Mailman/docs/switchboard.txt  1970-01-01 00:00:00 +
+++ b/Mailman/docs/switchboard.txt  2007-06-27 21:33:42 +
@@ -0,0 +1,151 @@
+The switchboard
+===
+
+The switchboard is subsystem that moves messages between queues.  Each
+instance of a switchboard is responsible for one queue directory.
+
+ from email import message_from_string
+ from Mailman.Message import Message
+ from Mailman.Queue.Switchboard import Switchboard
+ msg = message_from_string(\
+... From: [EMAIL PROTECTED]
+... To: [EMAIL PROTECTED]
+...
+... A test message.
+... , Message)
+
+Create a switchboard by giving its queue directory.
+
+ import os
+ from Mailman.configuration import config
+ queue_directory = os.path.join(config.QUEUE_DIR, 'test')
+ switchboard = Switchboard(queue_directory)
+ switchboard.queue_directory == queue_directory
+True
+
+Here's a helper function for ensuring things work correctly.
+
+ def check_qfiles():
+... files = {}
+... for qfile in os.listdir(queue_directory):
+... root, ext = os.path.splitext(qfile)
+... files[ext] = files.get(ext, 0) + 1
+... return sorted(files.items())
+
+
+Enqueing and dequeing
+-
+
+The message can be enqueued with metadata specified in the passed in
+dictionary.
+
+ filebase = switchboard.enqueue(msg)
+ check_qfiles()
+[('.pck', 1)]
+
+To read the contents of a queue file, dequeue it.
+
+ msg, msgdata = switchboard.dequeue(filebase)
+ print msg.as_string()
+From: [EMAIL PROTECTED]
+To: [EMAIL PROTECTED]
+BLANKLINE
+A test message.
+BLANKLINE
+ sorted(msgdata.items())
+[('_parsemsg', False), ('received_time', ...), ('version', 3)]
+ check_qfiles()
+[('.bak', 1)]
+
+To complete the dequeing process, removing all traces of the message file,
+finish it (without preservation).
+
+ switchboard.finish(filebase)
+ check_qfiles()
+[]
+
+When enqueing a file, you can provide additional metadata keys by using
+keyword arguments.
+
+ filebase = switchboard.enqueue(msg, {'foo': 1}, bar=2)
+ msg, msgdata = switchboard.dequeue(filebase)
+ switchboard.finish(filebase)
+ sorted(msgdata.items())
+[('_parsemsg', False),
+('bar', 2), ('foo', 1),
+('received_time', ...), ('version', 3)]
+
+Keyword arguments override keys from the metadata dictionary.
+
+ filebase = switchboard.enqueue(msg, {'foo': 1}, foo=2)
+ msg, msgdata = switchboard.dequeue(filebase)
+ switchboard.finish(filebase)
+ sorted(msgdata.items())
+[('_parsemsg', False),
+('foo', 2),
+('received_time', ...), ('version', 3)]
+
+
+Iterating over files
+
+
+There are two ways to iterate over all the files in a switchboard's queue.
+Normally, queue files end in .pck (for 'pickle') and the easiest way to
+iterate over just these files is to use the .files attribute.
+
+ filebase_1 = switchboard.enqueue(msg, foo=1)
+ filebase_2 = switchboard.enqueue(msg, foo=2)
+ filebase_3 = switchboard.enqueue(msg, foo=3)
+ filebases = sorted((filebase_1, filebase_2, filebase_3))
+ sorted(switchboard.files) == filebases
+True
+ check_qfiles()
+[('.pck', 3)]
+
+You can also use the .get_files() method if you want to iterate over all the
+file bases for some other extension.
+
+ for filebase in switchboard.get_files():
+... msg, msgdata = switchboard.dequeue(filebase)
+ bakfiles = sorted(switchboard.get_files('.bak'))
+ bakfiles == filebases
+True
+ check_qfiles()
+[('.bak', 3)]
+ for filebase in switchboard.get_files('.bak'):
+... switchboard.finish(filebase)
+ check_qfiles()
+[]
+
+
+Recovering files
+
+
+Calling 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-27 Thread noreply

revno: 6513
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Wed 2007-06-27 23:01:56 -0400
message:
  Repair docfiles broken with the new ISwitchboard interface.  Mostly these were
  due to the change from the method Switchboard.files() to the generator
  property Switchboard.files.
  
  Add support in cleaning_teardown() for the doctests for removing all queue
  files leftover after a test is complete.  This way, failures in one test that
  queues files won't break all subsequent such tests.
  
  Update the 2.1.9 section of the NEWS.txt file from the Mailman 2.1 branch.
modified:
  Mailman/docs/acknowledge.txt
  Mailman/docs/replybot.txt
  Mailman/testing/test_documentation.py
  docs/NEWS.txt

=== modified file 'Mailman/docs/acknowledge.txt'
--- a/Mailman/docs/acknowledge.txt  2007-06-22 21:15:03 +
+++ b/Mailman/docs/acknowledge.txt  2007-06-28 03:01:56 +
@@ -21,7 +21,7 @@
  # for new auto-response messages.
  from Mailman.Queue.sbcache import get_switchboard
  virginq = get_switchboard(config.VIRGINQUEUE_DIR)
- virginq.files()
+ list(virginq.files)
 []
 
 Subscribe a user to the mailing list.
@@ -44,7 +44,7 @@
 ...
 ... , Message)
  process(mlist, msg, {})
- virginq.files()
+ list(virginq.files)
 []
 
 We can also specify the original sender in the message's metadata.  If that
@@ -55,7 +55,7 @@
 ...
 ... , Message)
  process(mlist, msg, dict(original_sender='[EMAIL PROTECTED]'))
- virginq.files()
+ list(virginq.files)
 []
 
 
@@ -69,7 +69,7 @@
 ...
 ... , Message)
  process(mlist, msg, {})
- virginq.files()
+ list(virginq.files)
 []
 
 Similarly if the original sender is specified in the message metadata, and
@@ -83,7 +83,7 @@
  flush()
 
  process(mlist, msg, dict(original_sender='[EMAIL PROTECTED]'))
- virginq.files()
+ list(virginq.files)
 []
 
 
@@ -104,11 +104,11 @@
 ...
 ... , Message)
  process(mlist, msg, {})
-
- len(virginq.files())
+ files = list(virginq.files)
+ len(files)
 1
- qmsg, qdata = virginq.dequeue(virginq.files()[0])
- virginq.files()
+ qmsg, qdata = virginq.dequeue(files[0])
+ list(virginq.files)
 []
  # Print only some of the meta data.  The rest is uninteresting.
  qdata['listname']
@@ -143,11 +143,11 @@
 ...
 ... , Message)
  process(mlist, msg, {})
-
- len(virginq.files())
+ files = list(virginq.files)
+ len(files)
 1
- qmsg, qdata = virginq.dequeue(virginq.files()[0])
- virginq.files()
+ qmsg, qdata = virginq.dequeue(files[0])
+ list(virginq.files)
 []
  # Print only some of the meta data.  The rest is uninteresting.
  qdata['listname']

=== modified file 'Mailman/docs/replybot.txt'
--- a/Mailman/docs/replybot.txt 2007-06-22 21:15:03 +
+++ b/Mailman/docs/replybot.txt 2007-06-28 03:01:56 +
@@ -19,7 +19,7 @@
  # for new auto-response messages.
  from Mailman.Queue.sbcache import get_switchboard
  virginq = get_switchboard(config.VIRGINQUEUE_DIR)
- virginq.files()
+ list(virginq.files)
 []
 
 
@@ -43,9 +43,10 @@
 ... help
 ... , Message)
  process(mlist, msg, dict(toowner=True))
- len(virginq.files())
+ files = list(virginq.files)
+ len(files)
 1
- qmsg, qdata = virginq.dequeue(virginq.files()[0])
+ qmsg, qdata = virginq.dequeue(files[0])
  # Print only some of the meta data.  The rest is uninteresting.
  qdata['listname']
 '[EMAIL PROTECTED]'
@@ -66,7 +67,7 @@
 Precedence: bulk
 BLANKLINE
 admin autoresponse text
- virginq.files()
+ list(virginq.files)
 []
 
 
@@ -84,7 +85,7 @@
 ... help me
 ... , Message)
  process(mlist, msg, dict(toowner=True))
- virginq.files()
+ list(virginq.files)
 []
 
 Mailman itself can suppress autoresponses for certain types of internally
@@ -96,7 +97,7 @@
 ... help for you
 ... , Message)
  process(mlist, msg, dict(noack=True, toowner=True))
- virginq.files()
+ list(virginq.files)
 []
 
 If there is a Precedence: header with any of the values 'bulk', 'junk', or
@@ -109,16 +110,16 @@
 ... hey!
 ... , Message)
  process(mlist, msg, dict(toowner=True))
- virginq.files()
+ list(virginq.files)
 []
 
  msg.replace_header('precedence', 'junk')
  process(mlist, msg, dict(toowner=True))
- virginq.files()
+ list(virginq.files)
 []
  msg.replace_header('precedence', 'list')
  process(mlist, msg, dict(toowner=True))
- virginq.files()
+ list(virginq.files)
 []
 
 Unless the X-Ack: header has a value of yes, in which case, the Precedence
@@ -126,9 +127,10 @@
 
  msg['X-Ack'] = 'yes'
  process(mlist, msg, dict(toowner=True))
- len(virginq.files())
+ files = list(virginq.files)
+ len(files)
 1
- qmsg, qdata 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-27 Thread noreply

revno: 6514
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Thu 2007-06-28 01:11:50 -0400
message:
  Convert the rest of test_runners.py to doctests; even though incomplete, they
  test everything the old unit tests tested.  There are XXX's left in the
  doctests as reminders to flesh them out.
  
  Change the NNTP_REWRITE_DUPLICATE_HEADERS to use proper capitalization.
  
  Revert a change I made in the conversion of the Switchboard class:
  Switchboard.files is no longer a generator.  The Runner implementation is
  cleaner if this returns a concrete list, so that's what it does now.  Update
  the tests to reflect that.
  
  The Runner simplifies now too because it no longer needs _open_files() or the
  _listcache WeakValueDictionary.  The standard list manager handles all this
  now, so just use it directly.
  
  Also change the way the Runner sets the language context in _onefile().  It
  still tries to set it to the preferred language of the sender, if the sender
  is a member of the list.  Otherwise it sets it to the list's preferred
  language, not the system's preferred language.  Removed a conditional that
  can't possibly happen.
removed:
  Mailman/Queue/tests/test_runners.py
added:
  Mailman/docs/news-runner.txt
  Mailman/docs/runner.txt
modified:
  Mailman/Defaults.py.in
  Mailman/Queue/Runner.py
  Mailman/Queue/Switchboard.py
  Mailman/docs/acknowledge.txt
  Mailman/docs/replybot.txt
  Mailman/docs/switchboard.txt

=== removed file 'Mailman/Queue/tests/test_runners.py'
--- a/Mailman/Queue/tests/test_runners.py   2007-06-27 21:33:42 +
+++ b/Mailman/Queue/tests/test_runners.py   1970-01-01 00:00:00 +
@@ -1,163 +0,0 @@
-# Copyright (C) 2001-2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-Unit tests for the various Mailman qrunner modules.
-
-import os
-import email
-import shutil
-import tempfile
-import unittest
-
-from Mailman.Message import Message
-from Mailman.Queue.NewsRunner import prepare_message
-from Mailman.Queue.Runner import Runner
-from Mailman.Queue.Switchboard import Switchboard
-from Mailman.testing.base import TestBase
-
-
-
-class TestPrepMessage(TestBase):
-def test_remove_unacceptables(self):
-eq = self.assertEqual
-msg = email.message_from_string(\
-From: [EMAIL PROTECTED]
-To: [EMAIL PROTECTED]
-NNTP-Posting-Host: news.dom.ain
-NNTP-Posting-Date: today
-X-Trace: blah blah
-X-Complaints-To: [EMAIL PROTECTED]
-Xref: blah blah
-Xref: blah blah
-Date-Received: yesterday
-Posted: tomorrow
-Posting-Version: 99.99
-Relay-Version: 88.88
-Received: blah blah
-
-A message
-)
-msgdata = {}
-prepare_message(self._mlist, msg, msgdata)
-eq(msgdata.get('prepped'), 1)
-eq(msg['from'], '[EMAIL PROTECTED]')
-eq(msg['to'], '[EMAIL PROTECTED]')
-eq(msg['nntp-posting-host'], None)
-eq(msg['nntp-posting-date'], None)
-eq(msg['x-trace'], None)
-eq(msg['x-complaints-to'], None)
-eq(msg['xref'], None)
-eq(msg['date-received'], None)
-eq(msg['posted'], None)
-eq(msg['posting-version'], None)
-eq(msg['relay-version'], None)
-eq(msg['received'], None)
-
-def test_munge_duplicates_no_duplicates(self):
-eq = self.assertEqual
-msg = email.message_from_string(\
-From: [EMAIL PROTECTED]
-To: [EMAIL PROTECTED]
-Cc: [EMAIL PROTECTED]
-Content-Transfer-Encoding: yes
-
-A message
-)
-msgdata = {}
-prepare_message(self._mlist, msg, msgdata)
-eq(msgdata.get('prepped'), 1)
-eq(msg['from'], '[EMAIL PROTECTED]')
-eq(msg['to'], '[EMAIL PROTECTED]')
-eq(msg['cc'], '[EMAIL PROTECTED]')
-eq(msg['content-transfer-encoding'], 'yes')
-
-def test_munge_duplicates(self):
-eq = self.assertEqual
-msg = email.message_from_string(\
-From: [EMAIL PROTECTED]
-To: [EMAIL PROTECTED]
-To: [EMAIL PROTECTED]
-Cc: [EMAIL PROTECTED]
-Cc: [EMAIL PROTECTED]
-Cc: [EMAIL PROTECTED]
-Content-Transfer-Encoding: yes
-Content-Transfer-Encoding: no
-Content-Transfer-Encoding: maybe
-
-A message
-)
-msgdata = {}
-prepare_message(self._mlist, msg, msgdata)
-eq(msgdata.get('prepped'), 1)

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-23 Thread noreply

revno: 6511
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Sat 2007-06-23 08:38:45 -0400
message:
  Simplify doctests by having just a single test_documentation.py module in
  Mailman/testing.  This introspects the Mailman/docs directory and adds
  DocFileSuites for all .txt files found there.
removed:
  Mailman/testing/test_acknowledge.py
  Mailman/testing/test_address.py
  Mailman/testing/test_after_delivery.py
  Mailman/testing/test_avoid_duplicates.py
  Mailman/testing/test_calc_recips.py
  Mailman/testing/test_cleanse.py
  Mailman/testing/test_cook_headers.py
  Mailman/testing/test_decorate.py
  Mailman/testing/test_listmanager.py
  Mailman/testing/test_mlist_addresses.py
  Mailman/testing/test_replybot.py
  Mailman/testing/test_user.py
  Mailman/testing/test_usermanager.py
added:
  Mailman/testing/test_documentation.py
modified:
  Mailman/testing/base.py
  Mailman/testing/test_membership.py

=== removed file 'Mailman/testing/test_acknowledge.py'
--- a/Mailman/testing/test_acknowledge.py   2007-06-22 21:15:03 +
+++ b/Mailman/testing/test_acknowledge.py   1970-01-01 00:00:00 +
@@ -1,27 +0,0 @@
-# Copyright (C) 2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-Doctest harness for testing message acknowledgment.
-
-import unittest
-from Mailman.testing.base import make_docfile_suite
-
-
-def test_suite():
-suite = unittest.TestSuite()
-suite.addTest(make_docfile_suite('../docs/acknowledge.txt'))
-return suite

=== removed file 'Mailman/testing/test_address.py'
--- a/Mailman/testing/test_address.py   2007-06-22 21:15:03 +
+++ b/Mailman/testing/test_address.py   1970-01-01 00:00:00 +
@@ -1,27 +0,0 @@
-# Copyright (C) 2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-Doctest harness for testing IAddress interface.
-
-import unittest
-from Mailman.testing.base import make_docfile_suite
-
-
-def test_suite():
-suite = unittest.TestSuite()
-suite.addTest(make_docfile_suite('../docs/addresses.txt'))
-return suite

=== removed file 'Mailman/testing/test_after_delivery.py'
--- a/Mailman/testing/test_after_delivery.py2007-06-22 21:15:03 +
+++ b/Mailman/testing/test_after_delivery.py1970-01-01 00:00:00 +
@@ -1,28 +0,0 @@
-# Copyright (C) 2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-Doctest harness for testing bookkeeping done after message delivery.
-
-import unittest
-
-from Mailman.testing.base import make_docfile_suite
-
-
-def test_suite():
-suite = unittest.TestSuite()
-suite.addTest(make_docfile_suite('../docs/after-delivery.txt'))
-return suite

=== removed file 'Mailman/testing/test_avoid_duplicates.py'
--- a/Mailman/testing/test_avoid_duplicates.py  2007-06-22 21:15:03 +
+++ b/Mailman/testing/test_avoid_duplicates.py  

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-22 Thread noreply

revno: 6508
committer: Mark Sapiro [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Fri 2007-06-22 11:53:02 -0700
message:
  Scrubber.py - Malformed RFC 2047 encoded filename= parameter can have
a null byte or other garbage in the extension. Cleaned this.
  - Improved handling of None payloads.
  - Cleaned up a few charset coercions.
  OutgoingRunner.py - Made probe bounce processing and queuing of bounces
  conditional on having some permanent failure(s).
modified:
  Mailman/Handlers/Scrubber.py
  Mailman/Queue/OutgoingRunner.py

=== modified file 'Mailman/Handlers/Scrubber.py'
--- a/Mailman/Handlers/Scrubber.py  2007-05-28 20:21:41 +
+++ b/Mailman/Handlers/Scrubber.py  2007-06-22 18:53:02 +
@@ -277,6 +277,7 @@
 size = len(payload)
 url = save_attachment(mlist, part, dir)
 desc = part.get('content-description', _('not available'))
+desc = Utils.oneline(desc, lcset)
 filename = part.get_filename(_('not available'))
 filename = Utils.oneline(filename, lcset)
 replace_payload_by_text(part, _(\
@@ -318,23 +319,23 @@
 text.append(_('Skipped content of type %(partctype)s\n'))
 continue
 try:
-t = part.get_payload(decode=True)
+t = part.get_payload(decode=True) or ''
 # MAS: TypeError exception can occur if payload is None. This
 # was observed with a message that contained an attached
 # message/delivery-status part. Because of the special parsing
 # of this type, this resulted in a text/plain sub-part with a
 # null body. See bug 1430236.
 except (binascii.Error, TypeError):
-t = part.get_payload()
+t = part.get_payload() or ''
 # Email problem was solved by Mark Sapiro. (TK)
 partcharset = part.get_content_charset('us-ascii')
 try:
 t = unicode(t, partcharset, 'replace')
 except (UnicodeError, LookupError, ValueError, TypeError,
 AssertionError):
-# What is the cause to come this exception now ?
+# We can get here if partcharset is bogus in come way.
 # Replace funny characters.  We use errors='replace'.
-u = unicode(t, 'ascii', 'replace')
+t = unicode(t, 'ascii', 'replace')
 # Separation is useful
 if isinstance(t, basestring):
 if not t.endswith('\n'):
@@ -344,12 +345,11 @@
 charsets.append(partcharset)
 # Now join the text and set the payload
 sep = _('-- next part --\n')
-# The i18n separator is in the list's charset. Coerce it to the
-# message charset.
+# The i18n separator is in the list's charset. Coerce to unicode.
 try:
-s = unicode(sep, lcset, 'replace')
-sep = s.encode(charset, 'replace')
+sep = unicode(sep, lcset, 'replace')
 except (UnicodeError, LookupError, ValueError):
+# This shouldn't occur.
 pass
 rept = sep.join(text)
 # Replace entire message with text and scrubbed notice.
@@ -360,7 +360,9 @@
 try:
 replace_payload_by_text(msg, rept, charset)
 break
-except UnicodeError:
+# Bogus charset can throw several exceptions
+except (UnicodeError, LookupError, ValueError, TypeError,
+AssertionError):
 pass
 if format:
 msg.set_param('format', format)
@@ -396,7 +398,7 @@
 # i18n file name is encoded
 lcset = Utils.GetCharSet(mlist.preferred_language)
 filename = Utils.oneline(msg.get_filename(''), lcset)
-fnext = os.path.splitext(filename)[1]
+filename, fnext = os.path.splitext(filename)
 # For safety, we should confirm this is valid ext for content-type
 # but we can use fnext if we introduce fnext filtering
 if config.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION:
@@ -404,6 +406,8 @@
 ext = fnext or guess_extension(ctype, fnext)
 else:
 ext = guess_extension(ctype, fnext)
+# Allow only alphanumerics, dash, underscore, and dot
+ext = sre.sub('', ext)
 if not ext:
 # We don't know what it is, so assume it's just a shapeless
 # application/octet-stream, unless the Content-Type: is
@@ -421,7 +425,6 @@
 try:
 # Now base the filename on what's in the attachment, uniquifying it if
 # necessary.
-filename = msg.get_filename()
 if not filename or config.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME:
 filebase = 'attachment'
 else:
@@ -436,7 +439,8 @@
 # which one should we go 

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-22 Thread noreply
1 revision was removed from the branch.


--
(no title)
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
___
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org



[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-22 Thread noreply

revno: 6509
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Fri 2007-06-22 17:15:03 -0400
message:
  Refactor tests to get consistent clean up behavior.  The clean up sections
  have been removed from the .txt files and instead added to the test harness,
  which all doctests now use.
modified:
  Mailman/docs/ack-headers.txt
  Mailman/docs/acknowledge.txt
  Mailman/docs/addresses.txt
  Mailman/docs/after-delivery.txt
  Mailman/docs/avoid-duplicates.txt
  Mailman/docs/calc-recips.txt
  Mailman/docs/cleanse.txt
  Mailman/docs/cook-headers.txt
  Mailman/docs/membership.txt
  Mailman/docs/mlist-addresses.txt
  Mailman/docs/reply-to.txt
  Mailman/docs/replybot.txt
  Mailman/docs/subject-munging.txt
  Mailman/docs/usermanager.txt
  Mailman/docs/users.txt
  Mailman/testing/base.py
  Mailman/testing/test_acknowledge.py
  Mailman/testing/test_address.py
  Mailman/testing/test_after_delivery.py
  Mailman/testing/test_avoid_duplicates.py
  Mailman/testing/test_calc_recips.py
  Mailman/testing/test_cleanse.py
  Mailman/testing/test_cook_headers.py
  Mailman/testing/test_decorate.py
  Mailman/testing/test_listmanager.py
  Mailman/testing/test_membership.py
  Mailman/testing/test_mlist_addresses.py
  Mailman/testing/test_replybot.py
  Mailman/testing/test_user.py
  Mailman/testing/test_usermanager.py

=== modified file 'Mailman/docs/ack-headers.txt'
--- a/Mailman/docs/ack-headers.txt  2007-06-21 14:23:40 +
+++ b/Mailman/docs/ack-headers.txt  2007-06-22 21:15:03 +
@@ -53,13 +53,3 @@
 BLANKLINE
 A message of great import.
 BLANKLINE
-
-
-Clean up
-
-
- for mlist in config.list_manager.mailing_lists:
-... config.list_manager.delete(mlist)
- flush()
- list(config.list_manager.mailing_lists)
-[]

=== modified file 'Mailman/docs/acknowledge.txt'
--- a/Mailman/docs/acknowledge.txt  2007-06-18 18:53:34 +
+++ b/Mailman/docs/acknowledge.txt  2007-06-22 21:15:03 +
@@ -174,13 +174,3 @@
 List info page: http://lists.example.com/listinfo/[EMAIL PROTECTED]
 Your preferences: http://example.com/[EMAIL PROTECTED]
 BLANKLINE
-
-
-Clean up
-
-
- for mlist in config.list_manager.mailing_lists:
-... config.list_manager.delete(mlist)
- flush()
- list(config.list_manager.mailing_lists)
-[]

=== modified file 'Mailman/docs/addresses.txt'
--- a/Mailman/docs/addresses.txt2007-06-10 23:14:10 +
+++ b/Mailman/docs/addresses.txt2007-06-22 21:15:03 +
@@ -174,21 +174,3 @@
   [EMAIL PROTECTED] as MemberRole.member]
  sorted(mlist.digest_members.members)
 []
-
-
-Clean up
-
-
- for mlist in config.list_manager.mailing_lists:
-... config.list_manager.delete(mlist)
- for user in mgr.users:
-... mgr.delete_user(user)
- for address in mgr.addresses:
-... mgr.delete_address(address)
- flush()
- sorted(config.list_manager.names)
-[]
- sorted(mgr.users)
-[]
- sorted(mgr.addresses)
-[]

=== modified file 'Mailman/docs/after-delivery.txt'
--- a/Mailman/docs/after-delivery.txt   2007-06-18 18:53:34 +
+++ b/Mailman/docs/after-delivery.txt   2007-06-22 21:15:03 +
@@ -31,13 +31,3 @@
 True
  mlist.post_id
 11
-
-
-Clean up
-
-
- for mlist in config.list_manager.mailing_lists:
-... config.list_manager.delete(mlist)
- flush()
- list(config.list_manager.mailing_lists)
-[]

=== modified file 'Mailman/docs/avoid-duplicates.txt'
--- a/Mailman/docs/avoid-duplicates.txt 2007-06-21 23:46:27 +
+++ b/Mailman/docs/avoid-duplicates.txt 2007-06-22 21:15:03 +
@@ -171,13 +171,3 @@
 BLANKLINE
 Something of great import.
 BLANKLINE
-
-
-Clean up
-
-
- for mlist in config.list_manager.mailing_lists:
-... config.list_manager.delete(mlist)
- flush()
- list(config.list_manager.mailing_lists)
-[]

=== modified file 'Mailman/docs/calc-recips.txt'
--- a/Mailman/docs/calc-recips.txt  2007-06-19 15:13:09 +
+++ b/Mailman/docs/calc-recips.txt  2007-06-22 21:15:03 +
@@ -105,26 +105,3 @@
 * test_urgent_moderator()
 * test_urgent_admin()
 * test_urgent_reject()
-
-
-Clean up
-
-
- for member in mlist.members.members:
-... member.unsubscribe()
- flush()
- list(mlist.members.members)
-[]
- for user in config.user_manager.users:
-... config.user_manager.delete_user(user)
- for address in config.user_manager.addresses:
-... config.user_manager.delete_address(address)
- for mlist in config.list_manager.mailing_lists:
-... config.list_manager.delete(mlist)
- flush()
- list(config.user_manager.users)
-[]
- list(config.user_manager.addresses)
-[]
- list(config.list_manager.mailing_lists)
-[]

=== modified file 'Mailman/docs/cleanse.txt'
--- a/Mailman/docs/cleanse.txt  

[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)

2007-06-22 Thread noreply

revno: 6508
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: 3.0
timestamp: Fri 2007-06-22 15:32:57 -0400
message:
  Merge temporary Elixir model branch to the 3.0 trunk.  Some tests are failing
  now so I have to fix these before pushing the branch up.
removed:
  Mailman/database/model/rosterset.py
  Mailman/docs/mlist-rosters.txt
  Mailman/interfaces/rosterset.py
  Mailman/testing/test_mlist_rosters.py
added:
  .bzrignore
  Mailman/database/model/member.py
  Mailman/docs/ack-headers.txt
  Mailman/docs/acknowledge.txt
  Mailman/docs/after-delivery.txt
  Mailman/docs/avoid-duplicates.txt
  Mailman/docs/calc-recips.txt
  Mailman/docs/cleanse.txt
  Mailman/docs/cook-headers.txt
  Mailman/docs/membership.txt
  Mailman/docs/reply-to.txt
  Mailman/docs/subject-munging.txt
  Mailman/interfaces/member.py
  Mailman/testing/test_acknowledge.py
  Mailman/testing/test_after_delivery.py
  Mailman/testing/test_avoid_duplicates.py
  Mailman/testing/test_calc_recips.py
  Mailman/testing/test_cleanse.py
  Mailman/testing/test_cook_headers.py
renamed:
  Mailman/database/model/profile.py = Mailman/database/model/preferences.py
  Mailman/interfaces/profile.py = Mailman/interfaces/preferences.py
modified:
  Mailman/Defaults.py.in
  Mailman/Handlers/Acknowledge.py
  Mailman/Handlers/AfterDelivery.py
  Mailman/Handlers/AvoidDuplicates.py
  Mailman/Handlers/CalcRecips.py
  Mailman/Handlers/Cleanse.py
  Mailman/Handlers/CookHeaders.py
  Mailman/MailList.py
  Mailman/Utils.py
  Mailman/constants.py
  Mailman/database/listmanager.py
  Mailman/database/model/__init__.py
  Mailman/database/model/address.py
  Mailman/database/model/language.py
  Mailman/database/model/mailinglist.py
  Mailman/database/model/roster.py
  Mailman/database/model/user.py
  Mailman/database/model/version.py
  Mailman/database/types.py
  Mailman/database/usermanager.py
  Mailman/docs/addresses.txt
  Mailman/docs/listmanager.txt
  Mailman/docs/usermanager.txt
  Mailman/docs/users.txt
  Mailman/interfaces/address.py
  Mailman/interfaces/mlistrosters.py
  Mailman/interfaces/mlistweb.py
  Mailman/interfaces/roster.py
  Mailman/interfaces/user.py
  Mailman/interfaces/usermanager.py
  Mailman/testing/test_handlers.py
  Mailman/testing/test_membership.py
  Mailman/testing/test_user.py
  docs/NEWS.txt
  Mailman/database/model/preferences.py
  Mailman/interfaces/preferences.py

revno: 6504.1.13
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: model
timestamp: Thu 2007-06-21 19:46:27 -0400
message:
  Convert the AvoidDuplicates handler tests to a doctest.  Well,
  actually there /was/ no separate AvoidDuplicates test, but now there
  is, even though it may not be full coverage.
  
  Update the AvoidDuplicates handler to use more modern Python idioms.

revno: 6504.1.12
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: model
timestamp: Thu 2007-06-21 10:23:40 -0400
message:
  Convert the CookHeaders tests in test_handlers to using doctests, split up
  into several sub-documents.
  
  Defaults.py.in: Removed OLD_STYLE_PREFIXING.  So-called 'new style' 
prefixing
  is the default and only option now.
  
  CookHeaders.py is updated to the new API and some (but not all) of the 
code
  has been updated to more modern Python idioms.
  
  reply_goes_to_list attribute has been changed from a strict integer to a
  munepy enum called ReplyToMunging.
  
  RFC 2369 headers List-Subscribe and List-Unsubscribe now use the preferred
  -join and -leave addresses instead of the -request address with a subject
  value.

revno: 6504.1.11
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: model
timestamp: Tue 2007-06-19 18:42:27 -0400
message:
  Convert the Cleanse handler tests to doctest style.

revno: 6504.1.10
committer: Barry Warsaw [EMAIL PROTECTED]
branch nick: model
timestamp: Tue 2007-06-19 11:13:09 -0400
message:
  Convert the tests for the CalcRecips handler to doc tests.  There are
  some XXX's in the doc test because digest recipients aren't tested
  (though those may go in a different doctest), and neither are urgent
  messages.  This latter is for the same reason that the Approved
  handler is not yet tested; which password do you use in that header?
  
  The CalcRecips tests would also seem the natural place to test the
  receive_list_copy preference, but that actually gets processed in the
  AvoidDuplicates handler, so it isn't tested here.
  
  Add delivery_status (of type enum DeliveryStatus) to preferences.  I'm
  not entirely