[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 +