Projet

Général

Profil

0001-add-python3-compatibility-29418.patch

Paul Marillonnet, 17 janvier 2019 15:12

Télécharger (18,7 ko)

Voir les différences:

Subject: [PATCH] add python3 compatibility (#29418)

 setup.py                      |  6 +++---
 src/ldaptools/ldap_source.py  |  2 +-
 src/ldaptools/ldapsync/cmd.py | 28 ++++++++++++++------------
 src/ldaptools/ldif_utils.py   |  9 ++++-----
 src/ldaptools/slapd.py        | 15 +++++++++-----
 src/ldaptools/synchronize.py  | 38 ++++++++++++++++++++++-------------
 src/ldaptools/utils.py        | 33 ++++++++++++++++++++++++------
 tests/conftest.py             |  4 +++-
 tests/test_ldif_utils.py      |  7 +++++--
 tests/test_synchronize.py     |  9 ++++++---
 tox.ini                       |  5 +++--
 11 files changed, 101 insertions(+), 55 deletions(-)
setup.py
9 9

  
10 10
class eo_sdist(sdist):
11 11
    def run(self):
12
        print "creating VERSION file"
12
        print("creating VERSION file")
13 13
        if os.path.exists('VERSION'):
14 14
            os.remove('VERSION')
15 15
        version = get_version()
......
17 17
        version_file.write(version)
18 18
        version_file.close()
19 19
        sdist.run(self)
20
        print "removing VERSION file"
20
        print("removing VERSION file")
21 21
        if os.path.exists('VERSION'):
22 22
            os.remove('VERSION')
23 23

  
......
38 38
        else:
39 39
            result = '0.0.0-%s' % len(subprocess.check_output(
40 40
                ['git', 'rev-list', 'HEAD']).splitlines())
41
        return result.replace('-', '.').replace('.g', '+g')
41
        return result.decode('utf-8').replace('-', '.').replace('.g', '+g')
42 42
    return '0.0.0'
43 43

  
44 44

  
src/ldaptools/ldap_source.py
25 25
                continue
26 26
            entry = idict(entry)
27 27
            if 'objectclass' in entry:
28
                entry['objectclass'] = [istr(v) for v in entry['objectclass']]
28
                entry['objectclass'] = [istr(v.decode('utf-8')) for v in entry['objectclass']]
29 29
            yield dn, entry
30 30

  
31 31
    def __iter__(self):
src/ldaptools/ldapsync/cmd.py
1
from __future__ import print_function
2

  
1 3
import argparse
2 4
import sys
3 5

  
......
18 20
    def f(value):
19 21
        try:
20 22
            return f1(value)
21
        except argparse.ArgumentTypeError, e1:
23
        except argparse.ArgumentTypeError as e1:
22 24
            try:
23 25
                return f2(value)
24
            except argparse.ArgumentTypeError, e2:
26
            except argparse.ArgumentTypeError as e2:
25 27
                raise argparse.ArgumentTypeError('%s and %s' % (e1.args[0], e2.args[0]))
26 28
    return f
27 29

  
28 30

  
29 31
def object_class_pivot(value):
30
    value = filter(None, map(str.strip, map(str.lower, value.split())))
32
    value = list(filter(None, map(str.strip, map(str.lower, value.split()))))
31 33
    if len(value) != 2:
32 34
        raise argparse.ArgumentTypeError('%r is not a pair of an objectClass and an attribute name')
33 35
    return value
......
100 102
    attributes = list(attributes)
101 103
    if not attributes:
102 104
        parser.print_help()
103
        print 'Yout must give at least one attribute to synchronize'
105
        print('You must give at least one attribute to synchronize')
104 106

  
105 107
    if options.verbose:
106
        print 'Synchronizing ',
108
        print('Synchronizing', end=' ')
107 109
    if hasattr(options.source_uri, 'read'):
108 110
        if options.verbose:
109
            print options.source_uri.name,
111
            print(options.source_uri.name, end=' ')
110 112
        source = ldif_utils.ListLDIFParser(options.source_uri)
111 113
        source.parse()
112 114
    else:
113 115
        if options.verbose:
114
            print options.source_uri,
116
            print(options.source_uri, end=' ')
115 117
        conn = paged.PagedLDAPObject(options.source_uri)
116 118
        if options.source_uri.startswith('ldapi://'):
117 119
            conn.sasl_interactive_bind_s("", ldap.sasl.external())
......
122 124
                                       filterstr=options.source_filter)
123 125

  
124 126
    if options.verbose:
125
        print 'to', options.target_uri
127
        print('to', options.target_uri, end=' ')
126 128
    target_conn = paged.PagedLDAPObject(options.target_uri)
127 129
    if options.target_uri.startswith('ldapi://'):
128 130
        target_conn.sasl_interactive_bind_s("", ldap.sasl.external())
......
142 144
    synchronize.build_actions()
143 145
    if options.verbose:
144 146
        for action in synchronize.actions:
145
            print ' -', action
147
            print(' -', action)
146 148
        if not synchronize.actions:
147
            print 'Nothing to do.'
149
            print('Nothing to do.')
148 150
    if not options.fake:
149 151
        synchronize.apply_actions()
150 152
    failed_actions = [action for action in synchronize.actions if action.errors]
151 153
    if failed_actions:
152
        print >>sys.stderr, 'Some actions failed:'
154
        print('Some actions failed:', file=sys.stderr)
153 155
        for action in failed_actions:
154
            print ' -', action
156
            print(' -', action)
155 157
            for error in action.errors:
156
                print '  *', error
158
                print('  *', error)
157 159
        raise SystemExit(1)
src/ldaptools/ldif_utils.py
2 2
import ldif
3 3
from ldap.dn import dn2str
4 4

  
5
from ldaptools.utils import idict, str2dn
6

  
5
from ldaptools.utils import idict, str2dn, bytes2str_entry, str2bytes_entry
7 6

  
8 7
class AddError(Exception):
9 8
    pass
......
18 17
        dn = str2dn(dn)
19 18
        dn = [[(part[0].lower(),) + part[1:] for part in rdn] for rdn in dn]
20 19
        dn = dn2str(dn)
21
        self.entries.append((dn, entry))
20
        self.entries.append((dn, bytes2str_entry(entry)))
22 21

  
23 22
    def add(self, conn):
24 23
        for dn, entry in self.entries:
25 24
            try:
26
                conn.add_s(dn, ldap.modlist.addModlist(entry))
27
            except Exception, e:
25
                conn.add_s(dn, ldap.modlist.addModlist(str2bytes_entry(entry)))
26
            except Exception as e:
28 27
                raise AddError('error when adding %s' % dn, e)
29 28

  
30 29
    def __iter__(self):
src/ldaptools/slapd.py
1
import codecs
1 2
import time
2 3
import tempfile
3 4
import shutil
......
6 7
import ldap
7 8
import ldap.modlist
8 9
import ldap.sasl
9
import StringIO
10
try:
11
    from StringIO import StringIO
12
except ImportError:
13
    from io import StringIO
10 14
import atexit
11 15

  
12 16
from ldaptools.ldif_utils import ListLDIFParser
......
87 91
    process = None
88 92
    schemas = ['core', 'cosine', 'inetorgperson', 'nis', 'eduorg-200210-openldap', 'eduperson',
89 93
               'supann-2009']
90
    schemas_ldif = [open(os.path.join(os.path.dirname(__file__),
91
                                      'schemas', '%s.ldif' % schema)).read() for schema in schemas]
94
    schemas_ldif = [codecs.open(os.path.join(os.path.dirname(__file__),
95
                                      'schemas', '%s.ldif' % schema),
96
                                      encoding='utf-8').read() for schema in schemas]
92 97
    checkpoints = None
93 98
    data_dirs = None
94 99
    db_index = 1
......
172 177
        if context:
173 178
            ldif = ldif.format(**context)
174 179
        slapadd = self.create_process([SLAPADD_PATH, '-v', '-n%d' % db, '-F', self.config_dir])
175
        stdout, stderr = slapadd.communicate(input=ldif)
180
        stdout, stderr = slapadd.communicate(input=bytearray(ldif, 'utf-8'))
176 181
        assert slapadd.returncode == 0, 'slapadd failed: %s' % stderr
177 182

  
178 183
    def start(self):
......
266 271

  
267 272
        if context:
268 273
            ldif = ldif.format(**context)
269
        parser = ListLDIFParser(StringIO.StringIO(ldif))
274
        parser = ListLDIFParser(StringIO(ldif))
270 275
        parser.parse()
271 276
        conn = self.get_connection_admin()
272 277
        parser.add(conn)
src/ldaptools/synchronize.py
8 8
import ldap.dn
9 9

  
10 10

  
11
from .utils import batch_generator, to_dict_of_set, idict, str2dn, istr
11
from .utils import batch_generator, to_dict_of_set, idict, str2dn, istr, \
12
        bytes2str_entry, str2bytes_entry
12 13

  
13 14

  
14 15
@functools.total_ordering
......
48 49
        for msgid in self.msgids:
49 50
            try:
50 51
                self.results.append(conn.result2(msgid))
51
            except ldap.LDAPError, e:
52
            except ldap.LDAPError as e:
52 53
                self.errors.append(e)
53 54

  
54 55
    def __str__(self):
......
62 63
    order = 3
63 64

  
64 65
    def do(self, conn):
65
        self.msgids.append(conn.add(self.dn, ldap.modlist.addModlist(self.entry)))
66
        self.msgids.append(conn.add(self.dn, ldap.modlist.addModlist(str2bytes_entry(self.entry))))
66 67

  
67 68

  
68 69
class Rename(Action):
......
83 84

  
84 85
    def do(self, conn):
85 86
        modlist = []
86
        for key, values in self.entry.iteritems():
87
        for key, values in str2bytes_entry(self.entry).items():
87 88
            modlist.append((ldap.MOD_REPLACE, key, values))
88 89
        self.msgids.append(conn.modify(self.dn, modlist))
89 90

  
......
144 145
    def get_pivot_attribute(self, dn, entry):
145 146
        '''Find a pivot attribute value for an LDAP entry'''
146 147
        for objc, attr in self.pivot_attributes:
147
            entry['objectclass'] = map(istr, entry['objectclass'])
148
            if objc in entry['objectclass']:
148
            if istr(objc) in [istr(oc.decode('utf-8'))
149
                    if isinstance(oc, bytes) else oc
150
                    for oc in entry['objectclass']]:
149 151
                try:
150 152
                    value = entry[attr]
151 153
                except KeyError:
152 154
                    raise Exception('entry %s missing pivot attribute %s: %s' % (dn, attr, entry))
153 155
                break
154 156
        else:
155
            raise Exception('entry %s has unknown objectclasses %s' % (dn, entry['objectclass']))
157
            raise Exception('entry %s has unknown objectclasses %s' % (dn,
158
                    [objclass for objclass in entry['objectclass']]))
156 159
        if len(value) != 1:
157 160
            raise Exception('entry %s pivot attribute %s must have only one value' % (dn, attr))
161
        value = value[0]
162
        """
163
        may be used for input entries or output entries.
164
        decoding may be required
165
        """
166
        if isinstance(value, bytes):
167
            value = value.decode('utf-8')
158 168
        if attr in self.case_insensitive_attribute:
159
            value = map(istr, value)
160
        return objc, attr, value[0]
169
            value = istr(value)
170
        return objc, attr, value
161 171

  
162 172
    def get_target_entries(self, filterstr=None, attributes=[]):
163 173
        '''Return all target entries'''
164 174
        try:
165 175
            # Check base DN exist
166 176
            self.target_conn.search_s(self.target_dn, ldap.SCOPE_BASE)
167
            l = self.target_conn.paged_search_ext_s(self.target_dn, ldap.SCOPE_SUBTREE,
177
            res = self.target_conn.paged_search_ext_s(self.target_dn, ldap.SCOPE_SUBTREE,
168 178
                                                    filterstr=filterstr or self.all_filter,
169 179
                                                    attrlist=attributes)
170
            return ((dn, idict(entry)) for dn, entry in l if dn)
180
            return ((dn, idict(bytes2str_entry(entry))) for dn, entry in res if dn)
171 181
        except ldap.NO_SUCH_OBJECT:
172 182
            return []
173 183

  
......
215 225
                    seen_dn.add(out_dn)
216 226
                    self.rename(new_out_dn, target_dn)
217 227
                    renamed_dn[str2dn(new_out_dn)] = str2dn(target_dn)
218
                if to_dict_of_set(out_entry) != to_dict_of_set(entry):
228
                if to_dict_of_set(out_entry) != to_dict_of_set(bytes2str_entry(entry)):
219 229
                    new_entry = {}
220 230
                    for attribute in self.attributes:
221 231
                        if attribute in to_dict_of_set(entry):
......
233 243
        self.actions = []
234 244
        # Order source entries by DN depth
235 245
        entries = list(self.source)
236
        entries.sort(key=lambda (dn, entry): len(str2dn(dn)))
246
        entries.sort(key=lambda dn_entry: len(str2dn(dn_entry[0])))
237 247
        for dn, entry in entries:
238 248
            for key in entry.keys():
239
                if not key in self.attributes:
249
                if not str(key.lower()) in self.attributes:
240 250
                    del entry[key]
241 251
        # First create, rename and update
242 252
        for batch in batch_generator(entries, self.BATCH_SIZE):
src/ldaptools/utils.py
1 1
import ldap.dn
2
from six import string_types
2 3

  
3 4

  
4 5
# Copied from http://code.activestate.com/recipes/194371-case-insensitive-strings/
......
7 8
    Performs like str except comparisons are case insensitive."""
8 9

  
9 10
    def __init__(self, strMe):
10
        str.__init__(self, strMe)
11
        super(str, self).__init__()
11 12
        self.__lowerCaseMe = strMe.lower()
12 13

  
13 14
    def __repr__(self):
......
80 81
    def findkey(self, item):
81 82
        """A caseless way of checking if a key exists or not.
82 83
        It returns None or the correct key."""
83
        if not isinstance(item, str):
84
        if not isinstance(item, string_types):
84 85
            raise TypeError('Keywords for this object must be strings. You supplied %s' % type(item))
85 86
        key = item.lower()
86 87
        try:
......
147 148

  
148 149
    def has_key(self, item):
149 150
        """A case insensitive test for keys."""
150
        if not isinstance(item, str):
151
        if not isinstance(item, string_types):
151 152
            return False               # should never have a non-string key
152 153
        return item.lower() in self._keydict           # does the key exist
153 154

  
154 155
    def __contains__(self, item):
155 156
        """A case insensitive __contains__."""
156
        if not isinstance(item, str):
157
        if not isinstance(item, string_types):
157 158
            return False               # should never have a non-string key
158 159
        return item.lower() in self._keydict           # does the key exist
159 160

  
......
212 213
        return True
213 214

  
214 215
    def __ne__(self, other):
215
	return not (self == other)
216
        return not (self == other)
216 217

  
217 218

  
218 219
def batch_generator(gen, *batch_size):
......
229 230

  
230 231

  
231 232
def to_dict_of_set(d):
232
    r = idict({k: set(v) for k, v in d.iteritems()})
233
    r = idict({k: set(v) for k, v in d.items()})
233 234
    if 'objectclass' in r:
234 235
        r['objectclass'] = set(istr(v) for v in r['objectclass'])
235 236
    return r
......
237 238

  
238 239
def str2dn(s):
239 240
    return tuple(map(tuple, ldap.dn.str2dn(s)))
241

  
242

  
243
def bytes2str_entry(entry):
244
    str_entry = {}
245
    for key, values in entry.items():
246
        str_entry[key] = [v.decode('utf-8') if isinstance(v, bytes) else v for v in values]
247
    return str_entry
248

  
249
def str2bytes_entry(entry):
250
    bytes_entry = {}
251

  
252
    # python2 backward compat
253
    try:
254
        tested_types = basestring
255
    except NameError:
256
        tested_types = str
257

  
258
    for key, values in entry.items():
259
        bytes_entry[key] = [v.encode('utf-8') if isinstance(v, tested_types) else v for v in values]
260
    return bytes_entry
tests/conftest.py
1
from __future__ import print_function
2

  
1 3
import pytest
2 4
import tempfile
3 5
import os
......
98 100
    handle, path = tempfile.mkstemp()
99 101
    with open(path, 'w') as f:
100 102
        for attribute in attributes:
101
            print >>f, ' %s ' % attribute
103
            print(' %s ' % attribute, file=f)
102 104
        f.flush()
103 105
    def finalize():
104 106
        os.unlink(path)
tests/test_ldif_utils.py
1
import StringIO
1
try:
2
    from StringIO import StringIO
3
except ImportError:
4
    from io import StringIO
2 5

  
3 6
from ldaptools.ldif_utils import ListLDIFParser
4 7

  
5 8

  
6 9
def test_ldifparser():
7
    parser = ListLDIFParser(StringIO.StringIO('''dn: o=orga
10
    parser = ListLDIFParser(StringIO('''dn: o=orga
8 11
objectClass: organization
9 12

  
10 13
'''))
tests/test_synchronize.py
1
import StringIO
1
try:
2
    from StringIO import StringIO
3
except ImportError:
4
    from io import StringIO
2 5

  
3 6
import ldap
4 7

  
......
17 20
    conn = slapd.get_connection_admin()
18 21

  
19 22
    def syn_ldif(ldif):
20
        parser = ListLDIFParser(StringIO.StringIO(ldif))
23
        parser = ListLDIFParser(StringIO(ldif))
21 24
        parser.parse()
22 25
        synchronize = Synchronize(parser, 'o=orga', conn, 'o=orga',
23 26
                                  pivot_attributes=pivot_attributes,
......
163 166
    conn = slapd.get_connection_admin()
164 167

  
165 168
    def syn_ldif(ldif):
166
        parser = ListLDIFParser(StringIO.StringIO(ldif))
169
        parser = ListLDIFParser(StringIO(ldif))
167 170
        parser.parse()
168 171
        synchronize = Synchronize(parser, 'o=orga', conn, 'o=orga',
169 172
                                  pivot_attributes=pivot_attributes,
tox.ini
5 5

  
6 6
[tox]
7 7
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/ldaptools/
8
envlist = coverage,package
8
envlist = py2-coverage-ldap2,py{2,3}-coverage-ldap3,package
9 9

  
10 10
[testenv]
11 11
usedevelop = true
......
16 16
	pytest
17 17
	pytest-cov
18 18
	pytest-random
19
	python-ldap<3
19
	ldap3: python-ldap>2
20
	ldap2: python-ldap<3
20 21
commands =
21 22
	py.test {env:COVERAGE:} {posargs:--random tests}
22 23

  
23
-