在从源代码编译Python 2.7.15并安装到RedHat Enterprise 6.x之后,我注意到ldap.modlist在2.7.12和2.7.15之间存在显着差异。这对我们的Ansible实现(使用LDAP作为主机脚本的源)产生了重大影响。
以下示例代码:
import ldap.modlist as ml
d = { 'a': 'word', 'cn': 'ou=example,ou=com' }
e = { 'a': 'other word', 'cn': 'ou=example,ou=com'}
ldif = ml.modifyModlist(d, e)
print str(ldif)
2.7.12下的输出(看似正确)
[(1,'a',无),(0,'a','其他字')]
2.7.15下的输出(看似不正确)
[(1,'a',无),(0,'a',['o','t','h','e','r','','w','o', 'r','d'])]
这是一个已知的错误?我应该向谁提交这个? (GitHub的?)
这是2个ldap.modlist源文件之间的“差异”:
版本2.7.15是“>”版本2.7.12是“<”
PS1=> diff /Library/Python/2.7.12/site-packages/ldap/modlist.py /tmp/py2.7.15/build_modlist.py
4,10c4
< See http://www.python-ldap.org/ for details.
<
< $Id: modlist.py,v 1.18 2011/06/06 13:07:38 stroeder Exp $
<
< Python compability note:
< This module is known to work with Python 2.0+ but should work
< with Python 1.5.2 as well.
---
> See https://www.python-ldap.org/ for details.
15,31c9
< import string,ldap,ldap.cidict
<
<
< def list_dict(l,case_insensitive=0):
< """
< return a dictionary with all items of l being the keys of the dictionary
<
< If argument case_insensitive is non-zero ldap.cidict.cidict will be
< used for case-insensitive string keys
< """
< if case_insensitive:
< d = ldap.cidict.cidict()
< else:
< d = {}
< for i in l:
< d[i]=None
< return d
---
> import ldap
36c14
< ignore_attr_types = list_dict(map(string.lower,(ignore_attr_types or [])))
---
> ignore_attr_types = {v.lower() for v in ignore_attr_types or []}
38,39c16,17
< for attrtype in entry.keys():
< if ignore_attr_types.has_key(string.lower(attrtype)):
---
> for attrtype, value in entry.items():
> if attrtype.lower() in ignore_attr_types:
43c21
< attrvaluelist = filter(lambda x:x!=None,entry[attrtype])
---
> attrvaluelist = [item for item in value if item is not None]
45c23
< modlist.append((attrtype,entry[attrtype]))
---
> modlist.append((attrtype, value))
71,72c49,50
< ignore_attr_types = list_dict(map(string.lower,(ignore_attr_types or [])))
< case_ignore_attr_types = list_dict(map(string.lower,(case_ignore_attr_types or [])))
---
> ignore_attr_types = {v.lower() for v in ignore_attr_types or []}
> case_ignore_attr_types = {v.lower() for v in case_ignore_attr_types or []}
76,79c54,57
< attrtype_lower_map[string.lower(a)]=a
< for attrtype in new_entry.keys():
< attrtype_lower = string.lower(attrtype)
< if ignore_attr_types.has_key(attrtype_lower):
---
> attrtype_lower_map[a.lower()]=a
> for attrtype, value in new_entry.items():
> attrtype_lower = attrtype.lower()
> if attrtype_lower in ignore_attr_types:
83,84c61,62
< new_value = filter(lambda x:x!=None,new_entry[attrtype])
< if attrtype_lower_map.has_key(attrtype_lower):
---
> new_value = [item for item in value if item is not None]
> if attrtype_lower in attrtype_lower_map:
86c64
< old_value = filter(lambda x:x!=None,old_value)
---
> old_value = [item for item in old_value if item is not None]
97,110c75,81
< case_insensitive = case_ignore_attr_types.has_key(attrtype_lower)
< old_value_dict=list_dict(old_value,case_insensitive)
< new_value_dict=list_dict(new_value,case_insensitive)
< delete_values = []
< for v in old_value:
< if not new_value_dict.has_key(v):
< replace_attr_value = 1
< break
< add_values = []
< if not replace_attr_value:
< for v in new_value:
< if not old_value_dict.has_key(v):
< replace_attr_value = 1
< break
---
> if attrtype_lower in case_ignore_attr_types:
> old_value_set = {v.lower() for v in old_value}
> new_value_set = {v.lower() for v in new_value}
> else:
> old_value_set = set(old_value)
> new_value_set = set(new_value)
> replace_attr_value = new_value_set != old_value_set
120,121c91,92
< for a in attrtype_lower_map.keys():
< if ignore_attr_types.has_key(a):
---
> for a, val in attrtype_lower_map.items():
> if a in ignore_attr_types:
124c95
< attrtype = attrtype_lower_map[a]
---
> attrtype = val
来自Ldap community的明确反应
简短的回答是:
在Python 2.7.12中看到的行为(使用python-ldap == 2.4.38)是一种未定义和无意识的行为。 LDAP属性值始终是多值的(列表)。 python-ldap 3.1强制列表作为属性。您必须将属性字典写为{'a':['word'],'cn':['ou = example,ou = com']}。