add python3 compatibility (#29418)

This commit is contained in:
Paul Marillonnet 2019-01-10 18:33:25 +01:00
parent b6aef62901
commit 7d8c8e9429
12 changed files with 97 additions and 57 deletions

View File

@ -9,7 +9,7 @@ from setuptools.command.sdist import sdist
class eo_sdist(sdist):
def run(self):
print "creating VERSION file"
print("creating VERSION file")
if os.path.exists('VERSION'):
os.remove('VERSION')
version = get_version()
@ -17,7 +17,7 @@ class eo_sdist(sdist):
version_file.write(version)
version_file.close()
sdist.run(self)
print "removing VERSION file"
print("removing VERSION file")
if os.path.exists('VERSION'):
os.remove('VERSION')
@ -38,7 +38,7 @@ def get_version():
else:
result = '0.0.0-%s' % len(subprocess.check_output(
['git', 'rev-list', 'HEAD']).splitlines())
return result.replace('-', '.').replace('.g', '+g')
return result.decode('utf-8').replace('-', '.').replace('.g', '+g')
return '0.0.0'

View File

@ -25,7 +25,7 @@ class LDAPSource(object):
continue
entry = idict(entry)
if 'objectclass' in entry:
entry['objectclass'] = [istr(v) for v in entry['objectclass']]
entry['objectclass'] = [istr(v.decode('utf-8')) for v in entry['objectclass']]
yield dn, entry
def __iter__(self):

View File

@ -1,3 +1,5 @@
from __future__ import print_function
import argparse
import sys
@ -18,16 +20,16 @@ def or_type(f1, f2):
def f(value):
try:
return f1(value)
except argparse.ArgumentTypeError, e1:
except argparse.ArgumentTypeError as e1:
try:
return f2(value)
except argparse.ArgumentTypeError, e2:
except argparse.ArgumentTypeError as e2:
raise argparse.ArgumentTypeError('%s and %s' % (e1.args[0], e2.args[0]))
return f
def object_class_pivot(value):
value = filter(None, map(str.strip, map(str.lower, value.split())))
value = list(filter(None, map(str.strip, map(str.lower, value.split()))))
if len(value) != 2:
raise argparse.ArgumentTypeError('%r is not a pair of an objectClass and an attribute name')
return value
@ -100,18 +102,18 @@ Base DN of the source is remapped to another DN in the target directory''')
attributes = list(attributes)
if not attributes:
parser.print_help()
print 'Yout must give at least one attribute to synchronize'
print('You must give at least one attribute to synchronize')
if options.verbose:
print 'Synchronizing ',
print('Synchronizing', end=' ')
if hasattr(options.source_uri, 'read'):
if options.verbose:
print options.source_uri.name,
print(options.source_uri.name, end=' ')
source = ldif_utils.ListLDIFParser(options.source_uri)
source.parse()
else:
if options.verbose:
print options.source_uri,
print(options.source_uri, end=' ')
conn = paged.PagedLDAPObject(options.source_uri)
if options.source_uri.startswith('ldapi://'):
conn.sasl_interactive_bind_s("", ldap.sasl.external())
@ -122,7 +124,7 @@ Base DN of the source is remapped to another DN in the target directory''')
filterstr=options.source_filter)
if options.verbose:
print 'to', options.target_uri
print('to', options.target_uri, end=' ')
target_conn = paged.PagedLDAPObject(options.target_uri)
if options.target_uri.startswith('ldapi://'):
target_conn.sasl_interactive_bind_s("", ldap.sasl.external())
@ -142,16 +144,16 @@ Base DN of the source is remapped to another DN in the target directory''')
synchronize.build_actions()
if options.verbose:
for action in synchronize.actions:
print ' -', action
print(' -', action)
if not synchronize.actions:
print 'Nothing to do.'
print('Nothing to do.')
if not options.fake:
synchronize.apply_actions()
failed_actions = [action for action in synchronize.actions if action.errors]
if failed_actions:
print >>sys.stderr, 'Some actions failed:'
print('Some actions failed:', file=sys.stderr)
for action in failed_actions:
print ' -', action
print(' -', action)
for error in action.errors:
print ' *', error
print(' *', error)
raise SystemExit(1)

View File

@ -2,8 +2,7 @@ import ldap
import ldif
from ldap.dn import dn2str
from ldaptools.utils import idict, str2dn
from ldaptools.utils import idict, str2dn, bytes2str_entry, str2bytes_entry
class AddError(Exception):
pass
@ -18,13 +17,13 @@ class ListLDIFParser(ldif.LDIFParser):
dn = str2dn(dn)
dn = [[(part[0].lower(),) + part[1:] for part in rdn] for rdn in dn]
dn = dn2str(dn)
self.entries.append((dn, entry))
self.entries.append((dn, bytes2str_entry(entry)))
def add(self, conn):
for dn, entry in self.entries:
try:
conn.add_s(dn, ldap.modlist.addModlist(entry))
except Exception, e:
conn.add_s(dn, ldap.modlist.addModlist(str2bytes_entry(entry)))
except Exception as e:
raise AddError('error when adding %s' % dn, e)
def __iter__(self):

View File

@ -1,3 +1,4 @@
import codecs
import time
import tempfile
import shutil
@ -6,7 +7,10 @@ import os
import ldap
import ldap.modlist
import ldap.sasl
import StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import atexit
from ldaptools.ldif_utils import ListLDIFParser
@ -87,8 +91,9 @@ olcAccess: {{0}}to *
process = None
schemas = ['core', 'cosine', 'inetorgperson', 'nis', 'eduorg-200210-openldap', 'eduperson',
'supann-2009']
schemas_ldif = [open(os.path.join(os.path.dirname(__file__),
'schemas', '%s.ldif' % schema)).read() for schema in schemas]
schemas_ldif = [codecs.open(os.path.join(os.path.dirname(__file__),
'schemas', '%s.ldif' % schema),
encoding='utf-8').read() for schema in schemas]
checkpoints = None
data_dirs = None
db_index = 1
@ -172,7 +177,7 @@ olcAccess: {{0}}to * by * manage
if context:
ldif = ldif.format(**context)
slapadd = self.create_process([SLAPADD_PATH, '-v', '-n%d' % db, '-F', self.config_dir])
stdout, stderr = slapadd.communicate(input=ldif)
stdout, stderr = slapadd.communicate(input=bytearray(ldif, 'utf-8'))
assert slapadd.returncode == 0, 'slapadd failed: %s' % stderr
def start(self):
@ -266,7 +271,7 @@ olcAccess: {{0}}to * by * manage
if context:
ldif = ldif.format(**context)
parser = ListLDIFParser(StringIO.StringIO(ldif))
parser = ListLDIFParser(StringIO(ldif))
parser.parse()
conn = self.get_connection_admin()
parser.add(conn)

View File

@ -8,7 +8,8 @@ import ldap.modlist
import ldap.dn
from .utils import batch_generator, to_dict_of_set, idict, str2dn, istr
from .utils import batch_generator, to_dict_of_set, idict, str2dn, istr, \
bytes2str_entry, str2bytes_entry
@functools.total_ordering
@ -48,7 +49,7 @@ class Action(object):
for msgid in self.msgids:
try:
self.results.append(conn.result2(msgid))
except ldap.LDAPError, e:
except ldap.LDAPError as e:
self.errors.append(e)
def __str__(self):
@ -62,7 +63,7 @@ class Create(Action):
order = 3
def do(self, conn):
self.msgids.append(conn.add(self.dn, ldap.modlist.addModlist(self.entry)))
self.msgids.append(conn.add(self.dn, ldap.modlist.addModlist(str2bytes_entry(self.entry))))
class Rename(Action):
@ -83,7 +84,7 @@ class Update(Action):
def do(self, conn):
modlist = []
for key, values in self.entry.iteritems():
for key, values in str2bytes_entry(self.entry).items():
modlist.append((ldap.MOD_REPLACE, key, values))
self.msgids.append(conn.modify(self.dn, modlist))
@ -144,30 +145,39 @@ class Synchronize(object):
def get_pivot_attribute(self, dn, entry):
'''Find a pivot attribute value for an LDAP entry'''
for objc, attr in self.pivot_attributes:
entry['objectclass'] = map(istr, entry['objectclass'])
if objc in entry['objectclass']:
if istr(objc) in [istr(oc.decode('utf-8'))
if isinstance(oc, bytes) else oc
for oc in entry['objectclass']]:
try:
value = entry[attr]
except KeyError:
raise Exception('entry %s missing pivot attribute %s: %s' % (dn, attr, entry))
break
else:
raise Exception('entry %s has unknown objectclasses %s' % (dn, entry['objectclass']))
raise Exception('entry %s has unknown objectclasses %s' % (dn,
[objclass for objclass in entry['objectclass']]))
if len(value) != 1:
raise Exception('entry %s pivot attribute %s must have only one value' % (dn, attr))
value = value[0]
"""
may be used for input entries or output entries.
decoding may be required
"""
if isinstance(value, bytes):
value = value.decode('utf-8')
if attr in self.case_insensitive_attribute:
value = map(istr, value)
return objc, attr, value[0]
value = istr(value)
return objc, attr, value
def get_target_entries(self, filterstr=None, attributes=[]):
'''Return all target entries'''
try:
# Check base DN exist
self.target_conn.search_s(self.target_dn, ldap.SCOPE_BASE)
l = self.target_conn.paged_search_ext_s(self.target_dn, ldap.SCOPE_SUBTREE,
res = self.target_conn.paged_search_ext_s(self.target_dn, ldap.SCOPE_SUBTREE,
filterstr=filterstr or self.all_filter,
attrlist=attributes)
return ((dn, idict(entry)) for dn, entry in l if dn)
return ((dn, idict(bytes2str_entry(entry))) for dn, entry in res if dn)
except ldap.NO_SUCH_OBJECT:
return []
@ -215,7 +225,7 @@ class Synchronize(object):
seen_dn.add(out_dn)
self.rename(new_out_dn, target_dn)
renamed_dn[str2dn(new_out_dn)] = str2dn(target_dn)
if to_dict_of_set(out_entry) != to_dict_of_set(entry):
if to_dict_of_set(out_entry) != to_dict_of_set(bytes2str_entry(entry)):
new_entry = {}
for attribute in self.attributes:
if attribute in to_dict_of_set(entry):
@ -233,10 +243,10 @@ class Synchronize(object):
self.actions = []
# Order source entries by DN depth
entries = list(self.source)
entries.sort(key=lambda (dn, entry): len(str2dn(dn)))
entries.sort(key=lambda dn_entry: len(str2dn(dn_entry[0])))
for dn, entry in entries:
for key in entry.keys():
if not key in self.attributes:
if not str(key.lower()) in self.attributes:
del entry[key]
# First create, rename and update
for batch in batch_generator(entries, self.BATCH_SIZE):

View File

@ -1,4 +1,5 @@
import ldap.dn
from six import string_types
# Copied from http://code.activestate.com/recipes/194371-case-insensitive-strings/
@ -7,7 +8,7 @@ class istr(str):
Performs like str except comparisons are case insensitive."""
def __init__(self, strMe):
str.__init__(self, strMe)
super(str, self).__init__()
self.__lowerCaseMe = strMe.lower()
def __repr__(self):
@ -80,7 +81,7 @@ class idict(dict):
def findkey(self, item):
"""A caseless way of checking if a key exists or not.
It returns None or the correct key."""
if not isinstance(item, str):
if not isinstance(item, string_types):
raise TypeError('Keywords for this object must be strings. You supplied %s' % type(item))
key = item.lower()
try:
@ -147,13 +148,13 @@ class idict(dict):
def has_key(self, item):
"""A case insensitive test for keys."""
if not isinstance(item, str):
if not isinstance(item, string_types):
return False # should never have a non-string key
return item.lower() in self._keydict # does the key exist
def __contains__(self, item):
"""A case insensitive __contains__."""
if not isinstance(item, str):
if not isinstance(item, string_types):
return False # should never have a non-string key
return item.lower() in self._keydict # does the key exist
@ -212,7 +213,7 @@ class idict(dict):
return True
def __ne__(self, other):
return not (self == other)
return not (self == other)
def batch_generator(gen, *batch_size):
@ -229,7 +230,7 @@ def batch_generator(gen, *batch_size):
def to_dict_of_set(d):
r = idict({k: set(v) for k, v in d.iteritems()})
r = idict({k: set(v) for k, v in d.items()})
if 'objectclass' in r:
r['objectclass'] = set(istr(v) for v in r['objectclass'])
return r
@ -237,3 +238,17 @@ def to_dict_of_set(d):
def str2dn(s):
return tuple(map(tuple, ldap.dn.str2dn(s)))
def bytes2str_entry(entry):
str_entry = {}
for key, values in entry.items():
str_entry[key] = [v.decode('utf-8') if isinstance(v, bytes) else v for v in values]
return str_entry
def str2bytes_entry(entry):
bytes_entry = {}
for key, values in entry.items():
bytes_entry[key] = [v.encode('utf-8') if isinstance(v, string_types) else v for v in values]
return bytes_entry

View File

@ -1,3 +1,5 @@
from __future__ import print_function
import pytest
import tempfile
import os
@ -97,7 +99,7 @@ def attributes_path(request, attributes):
handle, path = tempfile.mkstemp()
with open(path, 'w') as f:
for attribute in attributes:
print >>f, ' %s ' % attribute
print(' %s ' % attribute, file=f)
f.flush()
def finalize():
os.unlink(path)

View File

@ -1,10 +1,13 @@
import StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from ldaptools.ldif_utils import ListLDIFParser
def test_ldifparser():
parser = ListLDIFParser(StringIO.StringIO('''dn: o=orga
parser = ListLDIFParser(StringIO('''dn: o=orga
objectClass: organization
'''))

View File

@ -36,8 +36,8 @@ def test_any(any_slapd):
def test_ssl_client_cert(slapd_ssl):
conn = slapd_ssl.get_connection_admin()
conn.modify_s('cn=config', [
(ldap.MOD_ADD, 'olcTLSCACertificateFile', slapd_ssl.tls[1]),
(ldap.MOD_ADD, 'olcTLSVerifyClient', 'demand'),
(ldap.MOD_ADD, 'olcTLSCACertificateFile', slapd_ssl.tls[1].encode('utf-8')),
(ldap.MOD_ADD, 'olcTLSVerifyClient', b'demand'),
])
with pytest.raises((ldap.SERVER_DOWN, ldap.CONNECT_ERROR)):

View File

@ -1,4 +1,7 @@
import StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import ldap
@ -17,7 +20,7 @@ def test_synchronize_ldif(slapd):
conn = slapd.get_connection_admin()
def syn_ldif(ldif):
parser = ListLDIFParser(StringIO.StringIO(ldif))
parser = ListLDIFParser(StringIO(ldif))
parser.parse()
synchronize = Synchronize(parser, 'o=orga', conn, 'o=orga',
pivot_attributes=pivot_attributes,
@ -163,7 +166,7 @@ def test_synchronize_deep_rename(slapd):
conn = slapd.get_connection_admin()
def syn_ldif(ldif):
parser = ListLDIFParser(StringIO.StringIO(ldif))
parser = ListLDIFParser(StringIO(ldif))
parser.parse()
synchronize = Synchronize(parser, 'o=orga', conn, 'o=orga',
pivot_attributes=pivot_attributes,

View File

@ -5,7 +5,7 @@
[tox]
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/ldaptools/
envlist = coverage,package
envlist = py2-coverage-ldap2,py{2,3}-coverage-ldap3,package
[testenv]
usedevelop = true
@ -16,7 +16,8 @@ deps =
pytest
pytest-cov
pytest-random
python-ldap<3
ldap3: python-ldap>2
ldap2: python-ldap<3
commands =
py.test {env:COVERAGE:} {posargs:--random tests}