After starting a thread on this list (http://mail.python.org/pipermail/mailman-developers/2006-May/018787.html> and also posting a request for feedback on Mailman-Users <http://mail.python.org/pipermail/mailman-users/2006-June/051588.html> which received no replies, I think I'm almost done with this. My patch is attached for review, comment.
Basically, I implement a new Utils.strip_verbose_pattern() function which converts verbose patterns to single-line, non-verbose equivalents and add code to versions.py for a one time conversion of existing topics regexps. I then modify the Gui, etc so that all future multi-line patterns are stored as entered, but compiled and used with the lines connected by '|'. Specifically, I would like advice and comment on two things. First since the strip_verbose_pattern() function is basically a one-time, list conversion function, would it be better added to versions.py rather than Utils.py. Second, when a topic pattern is first entered, an attempt is made to compile it, and if this throws an exception, the error is reported in the Gui and the topic is ignored. A similar thing is also done in MailList.py which will catch (eventually) and log bad topic regexps that might be entered with config_list or withlist. My question here is in the error message and log message, The pattern I report as invalid is the '|' joined pattern and not the original entry. I think this makes more sense, but I'm interested in what others think. Finally, in writing this post I have thought of another issue. Someone may have a saved config_list 'template' with old style 'verbose' topic regexps which wouldn't get converted if applied to a new 2.2.x list. Is this something to be concerned about? Note that this could be an issue any time a list attribute is deleted or has its set of values manipulated. -- Mark Sapiro <[EMAIL PROTECTED]> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan
Index: mailman/Mailman/Cgi/options.py =================================================================== --- mailman/Mailman/Cgi/options.py (revision 7907) +++ mailman/Mailman/Cgi/options.py (working copy) @@ -33,6 +33,7 @@ from Mailman.htmlformat import * +OR = '|' SLASH = '/' SETLANGUAGE = -1 @@ -1021,7 +1022,8 @@ table.AddRow([Bold(Label(_('Name:'))), Utils.websafe(name)]) table.AddRow([Bold(Label(_('Pattern (as regexp):'))), - '<pre>' + Utils.websafe(pattern) + '</pre>']) + '<pre>' + Utils.websafe(OR.join(pattern.splitlines())) + + '</pre>']) table.AddRow([Bold(Label(_('Description:'))), Utils.websafe(description)]) # Make colors look nice Index: mailman/Mailman/Gui/Topics.py =================================================================== --- mailman/Mailman/Gui/Topics.py (revision 7907) +++ mailman/Mailman/Gui/Topics.py (working copy) @@ -22,7 +22,9 @@ from Mailman.i18n import _ from Mailman.Gui.GUIBase import GUIBase +OR = '|' + class Topics(GUIBase): def GetConfigCategory(self): @@ -119,9 +121,10 @@ # Make sure the pattern was a legal regular expression name = Utils.websafe(name) try: - re.compile(pattern) + orpattern = OR.join(pattern.splitlines()) + re.compile(orpattern) except (re.error, TypeError): - safepattern = Utils.websafe(pattern) + safepattern = Utils.websafe(orpattern) doc.addError(_("""The topic pattern '%(safepattern)s' is not a legal regular expression. It will be discarded.""")) continue Index: mailman/Mailman/Handlers/Tagger.py =================================================================== --- mailman/Mailman/Handlers/Tagger.py (revision 7907) +++ mailman/Mailman/Handlers/Tagger.py (working copy) @@ -23,6 +23,7 @@ import email.Iterators import email.Parser +OR = '|' CRNL = '\r\n' EMPTYSTRING = '' NLTAB = '\n\t' @@ -51,7 +52,8 @@ # added to the specific topics bucket. hits = {} for name, pattern, desc, emptyflag in mlist.topics: - cre = re.compile(pattern, re.IGNORECASE | re.VERBOSE) + pattern = OR.join(pattern.splitlines()) + cre = re.compile(pattern, re.IGNORECASE) for line in matchlines: if cre.search(line): hits[name] = 1 Index: mailman/Mailman/MailList.py =================================================================== --- mailman/Mailman/MailList.py (revision 7907) +++ mailman/Mailman/MailList.py (working copy) @@ -74,6 +74,7 @@ _ = i18n._ EMPTYSTRING = '' +OR = '|' clog = logging.getLogger('mailman.config') elog = logging.getLogger('mailman.error') @@ -742,10 +743,11 @@ goodtopics = [] for name, pattern, desc, emptyflag in self.topics: try: - re.compile(pattern) + orpattern = OR.join(pattern.splitlines()) + re.compile(orpattern) except (re.error, TypeError): elog.error('Bad topic pattern "%s" for list: %s', - pattern, self.internal_name()) + orpattern, self.internal_name()) else: goodtopics.append((name, pattern, desc, emptyflag)) self.topics = goodtopics Index: mailman/Mailman/Utils.py =================================================================== --- mailman/Mailman/Utils.py (revision 7907) +++ mailman/Mailman/Utils.py (working copy) @@ -47,6 +47,7 @@ EMPTYSTRING = '' UEMPTYSTRING = u'' +CR = '\r' NL = '\n' DOT = '.' IDENTCHARS = ascii_letters + digits + '_' @@ -849,3 +850,58 @@ except (LookupError, UnicodeError, ValueError, HeaderParseError): # possibly charset problem. return with undecoded string in one line. return EMPTYSTRING.join(s.splitlines()) + + +def strip_verbose_pattern(pattern): + # Remove white space and comments from a verbose pattern and return a + # non-verbose, equivalent pattern. Replace CR and NL in the result + # with '\\r' and '\\n' respectively to avoid multi-line results. + if not isinstance(pattern, str): + return pattern + newpattern = '' + i = 0 + inclass = False + skiptoeol = False + copynext = False + while i < len(pattern): + c = pattern[i] + if copynext: + if c == NL: + newpattern += '\\n' + elif c == CR: + newpattern += '\\r' + else: + newpattern += c + copynext = False + elif skiptoeol: + if c == NL: + skiptoeol = False + elif c == '#' and not inclass: + skiptoeol = True + elif c == '[' and not inclass: + inclass = True + newpattern += c + copynext = True + elif c == ']' and inclass: + inclass = False + newpattern += c + elif re.search('\s', c): + if inclass: + if c == NL: + newpattern += '\\n' + elif c == CR: + newpattern += '\\r' + else: + newpattern += c + elif c == '\\' and not inclass: + newpattern += c + copynext = True + else: + if c == NL: + newpattern += '\\n' + elif c == CR: + newpattern += '\\r' + else: + newpattern += c + i += 1 + return newpattern Index: mailman/Mailman/Version.py =================================================================== --- mailman/Mailman/Version.py (revision 7907) +++ mailman/Mailman/Version.py (working copy) @@ -36,7 +36,7 @@ (REL_LEVEL << 4) | (REL_SERIAL << 0)) # config.pck schema version number -DATA_FILE_VERSION = 97 +DATA_FILE_VERSION = 98 # qfile/*.db schema version number QFILE_SCHEMA_VERSION = 3 Index: mailman/Mailman/versions.py =================================================================== --- mailman/Mailman/versions.py (revision 7907) +++ mailman/Mailman/versions.py (working copy) @@ -307,6 +307,15 @@ pass else: l.digest_members[k] = 0 + # + # Convert pre 2.2 topics regexps which were compiled in verbose mode + # to a non-verbose equivalent. + # + if stored_state['data_version'] <= 97 and stored_state.has_key('topics'): + l.topics = [] + for name, pattern, description, emptyflag in stored_state['topics']: + pattern = Utils.strip_verbose_pattern(pattern) + l.topics.append((name, pattern, description, emptyflag))
_______________________________________________ Mailman-Developers mailing list Mailman-Developers@python.org http://mail.python.org/mailman/listinfo/mailman-developers Mailman FAQ: http://www.python.org/cgi-bin/faqw-mm.py Searchable Archives: http://www.mail-archive.com/mailman-developers%40python.org/ Unsubscribe: http://mail.python.org/mailman/options/mailman-developers/archive%40jab.org Security Policy: http://www.python.org/cgi-bin/faqw-mm.py?req=show&file=faq01.027.htp