[Mailman-checkins] [Branch ~mailman-coders/mailman/3.0] (no title)
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)
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)
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 +