1import sys
2
3
4PY3 = sys.version_info[0] >= 3
5
6if PY3:
7    text_type = basestring = str
8    iterbytes = iter
9else: # PY2
10    text_type = unicode
11    basestring = basestring
12    import itertools
13    import functools
14    iterbytes = functools.partial(itertools.imap, ord)
15
16
17def ensure_binary(s, encoding="ascii", errors="strict"):
18    if isinstance(s, text_type):
19        return s.encode(encoding, errors)
20    elif isinstance(s, bytes):
21        return s
22    else:
23        raise TypeError("not expecting type '%s'" % type(s))
24
25
26def ensure_text(s, encoding="ascii", errors="strict"):
27    if isinstance(s, bytes):
28        return s.decode(encoding, errors)
29    elif isinstance(s, text_type):
30        return s
31    else:
32        raise TypeError("not expecting type '%s'" % type(s))
33
34
35# for python 2 on Windows we need to wrap the io.open function in order
36# to be able to reopen the standard stdin/stout streams in binary mode.
37# By detault, they are opened in 'text' mode by the MSVC runtime, so
38# we need to call setmode with the O_BINARY flag.
39# In Python 3 the binary flag is always set (python itself takes care
40# of the newline translation of standard streams)
41if PY3:
42    open = open
43else:
44    import io
45    try:
46        from msvcrt import setmode  # only available on Windows
47    except ImportError:
48        # on non-Windows platforms we can use the regular io.open
49        open = io.open
50    else:
51        import os
52
53        def open(file, mode='r', buffering=-1, encoding=None, errors=None,
54                 newline=None, closefd=True):
55            if isinstance(file, int):
56                # the 'file' argument is an integer file descriptor
57                fd = file
58                if fd < 0:
59                    raise ValueError('negative file descriptor')
60                if setmode:
61                    # `setmode` function sets the line-end translation and
62                    # returns the value of the previous mode
63                    fdcopy = os.dup(fd)
64                    current_mode = setmode(fdcopy, os.O_BINARY)
65                    if not (current_mode & os.O_BINARY):
66                        # the binary mode was not set: use the copy
67                        file = fdcopy
68                        if closefd:
69                            # close the original file descriptor
70                            os.close(fd)
71                        else:
72                            # ensure the copy is closed when file is closed
73                            closefd = True
74                    else:
75                        # original file already had binary flag, close copy
76                        os.close(fdcopy)
77
78            return io.open(
79                file, mode, buffering, encoding, errors, newline, closefd)
80
81
82try:
83    from enum import IntEnum
84except ImportError:
85    from collections import OrderedDict, namedtuple
86
87    # make do without the real Enum type, python3 only... :(
88    def IntEnum(typename, field_names, start=1):
89
90        @property
91        def __members__(self):
92            return OrderedDict([(k, getattr(self, k))
93                                for k in self._fields])
94
95        def __call__(self, value):
96            if value not in self:
97                raise ValueError("%s is not a valid %s" % (value, typename))
98            return value
99
100        base = namedtuple(typename, field_names)
101        attributes = {"__members__": __members__,
102                      "__call__": __call__}
103        klass = type(typename, (base,), attributes)
104        return klass._make(range(start, len(field_names) + start))
105