[Mailman-Developers] Mailman and Extend.py

C Nulk CNulk at scu.edu
Tue Apr 14 19:54:26 CEST 2009

Mark Sapiro wrote:
> C Nulk wrote:
>> While I waited, I did do some reading/researching on __init__ which lead
>> me to reading about __getattr__ and getattr.  If I understand correctly,
>> the LDAPMemberships uses them to get the isMember() and getMembers()
>> methods among others.
> Not exactly. Since the MemberAdaptor is replaceable, it can't simply be
> a super class of the MailList class. Thus, it's methods are not in the
> name space of the MailList object so we need an __getattr__ method to
> access them from whatever class is assigned as the list's
> _memberadaptor. So when you reference a method such as
> mlist.isMember() that is not directly in the name space of the mlist
> instance so mlist.__getattr__('isMember') is called (behind the
> scenes) and it finds mlist._memberadaptor.isMember() and returns that.

Sorry about that.  Your explanation is what I understood was the correct
action(s) for the interaction between MailList and the MemberAdaptor.  I
just way to short in my explanation.

> This is true whether _memberadaptor is the default OldStyleMemberships
> class or the LDAPMemberships class or something else. The only thing
> that is specific to LDAPMemberships is the fact that the list's
> extend.py module assigned LDAPMemberships to _memberadaptor.
>>> From then on, whenever a member adaptor method is called for that list,
>>> the method that is called is the one defined in LDAPMemberships.py.
>>> So, for example, if your Mailman version is 2.1.10 or later so that it
>>> supports the @LISTNAME entry in *_these_nonmembers, and you put say
>>> @list2 in accept_these_nonmembers of list1, list1's processing of a
>>> non-member post will call list2's isMember() method to see if the
>>> poster is a member of list2, and if list2 already uses
>>> LDAPMemberships, that's it - it's isMember() method will use its LDAP
>>> database.
>> Unfortunately, I am stuck at v2.1.9 for now.
>>> See the matches_p function in Mailman/Handlers/Moderate.py for more
>>> detail.
>> I did look at Mailman/Handlers/Moderate.py, specifically the matches_p
>> function.  What I envisioned doing was to modify the matches_p function
>> to single out "ldap" entries similar to the regex entries.  Then for
>> each "ldap" entry, call an LDAP2Dict function (to be written) which
>> returns a dictionary of email addresses, then check if the sender was in
>> the returned dictionary.
> That would work, but if this dictionary could be equated to the
> membership of some list, it seems to me that it would be easier to
> just backport the @listname feature to 2.1.9. You can find the
> original patch at
> <http://sourceforge.net/tracker/?func=detail&aid=1220144&group_id=103&atid=300103>.
> The 2.1.10 implementation is a bit more elaborate, but the SourceForge
> patch should be sufficient for your purpose.
> Note that there is nothing magic about the dictionary in
>     plainaddrs = [addr for addr in nonmembers if not
>                                    addr.startswith('^')]
>     addrdict = Utils.List2Dict(plainaddrs, foldcase=1)
>     if addrdict.has_key(sender):
>         return 1
> This could just as easily have been spelled
>     plainaddrs = [addr.lower() for addr in nonmembers if not
>                                    addr.startswith('^')]
>     if sender in plainaddrs:
>         return 1

I will have to take a closer look at the patch.  And, I probably will
apply it.  From the quick glance on sourceforge, it looks like it should
work for v2.1.9.  All I should have to do is adjust the diff lines to
point to the actual locations.

Being able to use another list as a source is great.  I would still like
to try to have everything defined/set within the list itself without
cross-referencing other "management" lists.  For us, it would be easier
to manage.

>> The changes made to LDAPMemberships.py would help since you explained to
>> me that one of your changes was to make members a dictionary.  The
>> getMembers() method would essentially return that dictionary.
> No. getMembers() MUST return a list or everything breaks.

Okay.  My mistake.  Still trying to figure out Python and how things
work.  Since getMembers() returns a list, I suppose I could use the
List2Dict() function to convert the list to a dictionary to be returned
by my LDAP2Dict() function.

>> The key would be the changes to extend.py so everything works.
> If you are accessing some LDAP directly (not via the @list construct)
> in the matches_p function, extend.py has nothing to do with it.

You will have to correct me if I am really off-base here on my

The extend.py module defines the extend() function which appears to set
ldap (as an object of the LDAPMemberships class)
   ldap = LDAPMemberships(list)
along with additional definitions, like
   ldap.ldapsearch = "(uid=somebody)"
   ldap.ldapserver = ldap.example.net
and to tie it to the list, set the _memberadaptor to the object by
   list._memberadaptor = ldap

So when the list membership or some other method is accessed and not in
the list namespace, your explanation above applies.

My idea of changing extend.py was to add additional objects of the
LDAPMemberships class.  Say an ldap2 and ldap3 like
    ldap2 = LDAPMemberships(list)
    ldap2.ldapsearch = "(uid=person1)"
    ldap2.ldapserver = ldap2.example.net
    list.<ldap2_identifier> = ldap2
    ldap3 = LDAPMemberships(list)
    ldap3.ldapsearch = "(role=list-poster)"     # as an example
    ldap3.ldapserver = ldap3.example.net
    list.<ldap3_identifier> = ldap3

both <ldap2_identifier> and <ldap3_identifier> are similar but not the
same as the _memberadapter.  The difference is <ldap2_identifier> and
<ldap3_identifier> are specified in the accept_these_nonmembers (and the
others) lists as LDAP=<ldap2_identifier> and likewise for any other
"LDAP=" identifier.

The matches_p function (a parameter change would be needed, explained
later) can separate out the "LDAP=" items and pass to the LDAP2Dict()
function the list and ldap identifier.

The LDAP2Dict() function would use getattr(list, ldap-identifier) to
locate the appropriate object of LDAPMemberships class.

So, for ldap2_identifier passed to LDAP2Dict(),  getattr(list,
ldap2_identifier) would return

Since I know that list.ldap2_identifier is an object with the
getMembers() method, I should be able to get the "members" of the ldap2
search.  And given that getMembers() returns a list, I can convert to a
dictionary or possibly use List2Dict() to return the dictionary.

I do know that the list is not passed to the matches_p() function.  For
this to work, it would be added to the parameter list so the list can be
passed to the LDAP2Dict() function.

>>> I hope this helps. If you still have questions, keep asking.
>> It has helped.  I have made some changes to Mailman/Handlers/Moderate.py
>> and Utils.py (to add the LDAP2Dict function).  Let me know if you would
>> like to see what I came up with and I can send the diffs and
>> explaination to you off-list.  You may have a better way to implement
>> what I am doing.  Well, actually, you probably do have a better way. :)
> See my remarks above.

I hope my explanations help you figure out what I am trying to do.  The
diffs may help you understand what I am trying to do.

I appreciate the help.

Thank you,

More information about the Mailman-Developers mailing list