[Mailman-Developers] Batch-level list configuration

David Champion dgc at uchicago.edu
Fri May 4 07:53:11 CEST 2007


* On 2007.05.02, in <83EB3FBBDF867245A54F47D4136B68C5D0E4B5 at PHXDCEX012.phx-dc.dhl.com>,
*	"Jeff Kunzelman (DHL)" <Jeff.Kunzelman at dhl.com> wrote: 
> 
> Is there a way that I can use the withlist command to change the display
> name of a mailman list?

You can, but you must write valid python code that manipulates
properties of the list object.  Withlist's function is to instantiate a
list object to facilitate these manipulations.


This attached program is something we developed here for uchicago.edu
some years ago, and have been using for a variety of batch and/or bulk
manipulations of list properties.  It's a bit more convenient than
withlist or the config_list program that appears is more recent Mailman
versions.  In short, you can use configlist to display or set any list
property *entirely on the command line*.  Values are displayed as
python expressions, and when setting a value, it must be set to a valid
expression, but you don't need to write any actual code.

I don't think that I've posted this before -- I'm sorry if I'm wrong
about that.  Feel free to use it without any warranty, etc.

Examples:
$ configlist.py mylist
[ shows all properties of the list as python expressions ]

$ configlist.py mylist description
description: 'This is the display name as shown on the listinfo page.'

$ configlist.py mylist description="'The mylist list is for me.'"
[ sets description.  Note the redundant quoting to protect the python
  expression from shell. ]

$ configlist.py mylist description=s/mylist/MyList/
[ use the s/// expression to munge the existing value of description ]


Configlist.py can be rather dangerous, because it allows changing any
list property at all without any of the constraints that may be built
into the web UI.  Be sure you know what you're doing.  You can use
configlist.py before and after a web UI change to get a feel for how to
script the same change in the future.

I apologize that the code is probably horrific to python enthusiasts.
It was among my first efforts and I've not revisited it since actually
learning the language.

-- 
 -D.    dgc at uchicago.edu, an Element of NSIT    University of Chicago
-------------- next part --------------
#!/opt/bin/python
##
## Sets/gets a mailing list's parameters.  Any of them.
## Danger, Will Robinson.
##
## $Id: configlist.py,v 1.7 2004/01/20 21:19:19 dgc Exp $
##

"""usage: configlist.py [-h|--help]
       configlist.py [-n|--not-really] listname
       configlist.py [-n|--not-really] listname param ...
       configlist.py [-n|--not-really] listname param=value ...
       configlist.py [-n|--not-really] listname param=/rx/value/flags ...
       configlist.py [-n|--not-really] listname param=s/rx/subst/flags ...
       configlist.py [-l|--no-lock]    ....

NOTE WELL: so that we can support all data types, parameter assignment
must follow Python syntax.  In particular, this means that string
literals are quoted.  For example:
	configlist.py list numeric_value=3
	configlist.py list string_value="'foo'"
        configlist.py list array_value="['foo', 'bar']"
	configlist.py list dict_value="{'num': 3, 'str': 'foo'}" 
	configlist.py list any_value="s/foO/bar/ig"

See %(myname)s for more usage details.
"""

#
# + If no parameters are named, the entire dictionary is dumped.
# + If a parameter has no predicate, that parameter is displayed.
# + With an "=value" predicate, that value is assigned.
# + With an "=/rx/value/flags" predicate, the value is assigned if the regular
#   expression matches. "flags" are the usual regex flags.
# + With an "=s/rx/subst/flags" predicate, a substitition is applied to the
#   portion of the original value which matches the regex. Flags are the usual.
# 
# With the -n flag, all the same work is done and results displayed; but the
# changes are not saved.
#
# regex matches apply to all the stringified python data, not just values.
# this is particularly relevant for dictionaries: the dict keys are subject
# to the substs just as the dict values are.
#

import os
import sys
sys.path.insert(0, "/opt/pkgs/mailman")

import getopt
import string
import grp
import pwd
import paths
import re
from Mailman.MailList import MailList, Errors

myname = sys.argv[0]

def usage(msg=''):
	print __doc__ % globals()
	if msg:
		print "Error: " + msg
	sys.exit(1)


def dump_params(list):
	## get keys, sort
	a = list.__dict__.keys()
	a.sort()
	## iterate and print
	for param in a:
		print param + ": " + eval("repr(list." + param + ")")


def do_params(list, args):
	changed = -1
	for arg in args:
		if string.find(arg, '=/') > -1:
			try:
				param, more = string.split(arg, '=/')
			except:
				usage()

			try:
				search, replace, flags = string.split(more, '/')
			except:
				usage()

			if flags:
				rx = re.compile(search + "(?" + string.lower(flags) +")")
			else:
				rx = re.compile(search, 0)

			value = eval("repr(list." + param + ")")
			if rx.search(value):
				changed = 1
				exec("list." + param + " = " + replace)

			print param + ": " + eval("repr(list." + param + ")")

		elif string.find(arg, "=s/") > -1:
			try:
				param, more = string.split(arg, '=s/')
			except:
				usage()

			try:
				search, replace, flags = string.split(more, '/')
			except:
				usage()

			value = eval("repr(list." + param + ")")
			if flags:
				rx = re.compile(search + "(?" + string.lower(flags) +")")
			else:
				rx = re.compile(search, 0)

			nvalue = rx.sub(replace, value);
			if value != nvalue:
				changed = 1
				exec("list." + param + " = " + nvalue)

			print param + ": " + eval("repr(list." + param + ")")

		elif string.find(arg, '=') > -1:
			param, value = string.split(arg, '=')
			exec("list." + param + " = " + value)
			print param + ": " + eval("repr(list." + param + ")")
			changed = 1

		else:
			print eval("list." + arg)
			changed = 0

	return changed


def main():
	cautious = 0
	dolock = 1

	try:
		opts, args = getopt.getopt(sys.argv[1:], 'hnl', ['help', 'not-really', 'no-lock'])

	except getopt.error, msg:
		usage(msg)

	if len(args) < 1:
		usage('A list name must be supplied.')

	name = args[0]

	for opt, arg in opts:
		if opt in ('-h', '--help'):
			usage()
		if opt in ('-n', '--not-really'):
			cautious = 1
		if opt in ('-l', '--no-lock'):
			dolock = 0


	## Load the list, or else emit an error immediately.
	try:
		list = MailList(name, 0)

	except Errors.MMUnknownListError, msg:
		usage(msg)


	## Find appropriate user information.
	group = grp.getgrnam('mailman')
	gid = group[2]
	passwd = pwd.getpwnam('mailman')
	uid = passwd[2]

	## Lock the list.
	if dolock:
		list.Lock()

	## If no parameters named, dump all dictionary pairs.
	if len(args) == 1:
		dump_params(list)
		changed = 0

	## For each param, try to split on "=", and get or set accordingly.
	else:
		changed = do_params(list, args[1:])


	## If something was altered, save the list and set permissions.
	if changed:
		rc = 0

		if cautious:
			print "List " + name + " not saved (cautious)"
		else:
			print "Saving changed list \"" + name + "\"."
			list.Save()
			os.chown(list._full_path + "/config.db", uid, gid)
			os.chmod(list._full_path + "/config.db", 00664)
	elif changed < 0:
		print "List " + name + " unchanged."
		rc = 1

	else:
		rc = 0


	## Unlock and quit.
	if dolock:
		list.Unlock()
	sys.exit(rc)

main()

##
## $Log: configlist.py,v $
## Revision 1.7  2004/01/20 21:19:19  dgc
## regex operations added
## -n (test mode) flag added
## help text updated to soothe complexity of syntax
##
## Revision 1.6  2001/06/07 23:04:31  dgc
## A touch further documentation in the help text.
##
## Revision 1.5  2001/06/06 13:18:08  dgc
## Renamed list_param.py to configlist.py -- a little more conventional
##
## Revision 1.4  2001/06/06 08:42:05  dgc
## path fix
##
## Revision 1.3  2001/06/06 07:39:08  dgc
## No longer prints "Saving list (foo)" on save
##
## Revision 1.2  2001/06/06 07:35:49  dgc
## cleaned up code a bit, added permission & owner fixes on save
##


More information about the Mailman-Developers mailing list