summaryrefslogtreecommitdiff
path: root/python/samba/compat.py
blob: 3762ca441e1f24e6b19298b5ddc9713ebb2d6f59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# module which helps with porting to Python 3
#
# Copyright (C) Lumir Balhar <lbalhar@redhat.com> 2017
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""module which helps with porting to Python 3"""

import sys

PY3 = sys.version_info[0] == 3

if PY3:
    # Sometimes in PY3 we have variables whose content can be 'bytes' or
    # 'str' and we can't be sure which. Generally this is because the
    # code variable can be initialised (or reassigned) a value from different
    # api(s) or functions depending on complex conditions or logic. Or another
    # common case is in PY2 the variable is 'type <str>' and in PY3 it is
    # 'class <str>' and the function to use e.g. b64encode requires 'bytes'
    # in PY3. In such cases it would be nice to avoid excessive testing in
    # the client code. Calling such a helper function should be avoided
    # if possible but sometimes this just isn't possible.
    # If a 'str' object is passed in it is encoded using 'utf8' or if 'bytes'
    # is passed in it is returned unchanged.
    # Using this function is PY2/PY3 code should ensure in most cases
    # the PY2 code runs unchanged in PY2 whereas the code in PY3 possibly
    # encodes the variable (see PY2 implementation of this function below)
    def get_bytes(bytesorstring):
        tmp = bytesorstring
        if isinstance(bytesorstring, str):
            tmp = bytesorstring.encode('utf8')
        elif not isinstance(bytesorstring, bytes):
            raise ValueError('Expected byte or string for %s:%s' % (type(bytesorstring), bytesorstring))
        return tmp

    # helper function to get a string from a variable that maybe 'str' or
    # 'bytes' if 'bytes' then it is decoded using 'utf8'. If 'str' is passed
    # it is returned unchanged
    # Using this function is PY2/PY3 code should ensure in most cases
    # the PY2 code runs unchanged in PY2 whereas the code in PY3 possibly
    # decodes the variable (see PY2 implementation of this function below)
    def get_string(bytesorstring):
        tmp = bytesorstring
        if isinstance(bytesorstring, bytes):
            tmp = bytesorstring.decode('utf8')
        elif not isinstance(bytesorstring, str):
            raise ValueError('Expected byte of string for %s:%s' % (type(bytesorstring), bytesorstring))
        return tmp

    def cmp_fn(x, y):
        """
        Replacement for built-in function cmp that was removed in Python 3

        Compare the two objects x and y and return an integer according to
        the outcome. The return value is negative if x < y, zero if x == y
        and strictly positive if x > y.
        """

        return (x > y) - (x < y)
    # compat functions
    from functools import cmp_to_key as cmp_to_key_fn

    # compat types
    integer_types = int,
    string_types = str
    text_type = str
    binary_type = bytes

    # alias
    import io
    StringIO = io.StringIO
    def ConfigParser(defaults=None, dict_type=dict, allow_no_value=False):
        from configparser import ConfigParser
        return ConfigParser(defaults, dict_type, allow_no_value, interpolation=None)
else:
    # Helper function to return bytes.
    # if 'unicode' is passed in then it is decoded using 'utf8' and
    # the result returned. If 'str' is passed then it is returned unchanged.
    # Using this function is PY2/PY3 code should ensure in most cases
    # the PY2 code runs unchanged in PY2 whereas the code in PY3 possibly
    # encodes the variable (see PY3 implementation of this function above)
    def get_bytes(bytesorstring):
        tmp = bytesorstring
        if isinstance(bytesorstring, unicode):
            tmp = bytesorstring.encode('utf8')
        elif not isinstance(bytesorstring, str):
            raise ValueError('Expected string for %s:%s' % (type(bytesorstring), bytesorstring))
        return tmp

    # Helper function to return string.
    # if 'str' or 'unicode' passed in they are returned unchanged
    # otherwise an exception is generated
    # Using this function is PY2/PY3 code should ensure in most cases
    # the PY2 code runs unchanged in PY2 whereas the code in PY3 possibly
    # decodes the variable (see PY3 implementation of this function above)
    def get_string(bytesorstring):
        tmp = bytesorstring
        if not(isinstance(bytesorstring, str) or isinstance(bytesorstring, unicode)):
            raise ValueError('Expected str or unicode for %s:%s' % (type(bytesorstring), bytesorstring))
        return tmp


    if sys.version_info < (2, 7):
        def cmp_to_key_fn(mycmp):

            """Convert a cmp= function into a key= function"""
            class K(object):
                __slots__ = ['obj']

                def __init__(self, obj, *args):
                    self.obj = obj

                def __lt__(self, other):
                    return mycmp(self.obj, other.obj) < 0

                def __gt__(self, other):
                    return mycmp(self.obj, other.obj) > 0

                def __eq__(self, other):
                    return mycmp(self.obj, other.obj) == 0

                def __le__(self, other):
                    return mycmp(self.obj, other.obj) <= 0

                def __ge__(self, other):
                    return mycmp(self.obj, other.obj) >= 0

                def __ne__(self, other):
                    return mycmp(self.obj, other.obj) != 0

                def __hash__(self):
                    raise TypeError('hash not implemented')
            return K
    else:
        from functools import cmp_to_key as cmp_to_key_fn

    # compat types
    integer_types = (int, long)
    string_types = basestring
    text_type = unicode
    binary_type = str

    # alias
    import cStringIO
    StringIO = cStringIO.StringIO
    from ConfigParser import ConfigParser
    cmp_fn = cmp