1# -*- coding: utf-8 -*-
2#
3# Copyright © 2012-2013 Pierre Raybaut
4# Licensed under the terms of the MIT License
5# (see spyderlib/__init__.py for details)
6
7"""
8spyderlib.py3compat
9-------------------
10
11Transitional module providing compatibility functions intended to help
12migrating from Python 2 to Python 3.
13
14This module should be fully compatible with:
15    * Python >=v2.6
16    * Python 3
17"""
18
19from __future__ import print_function
20
21import sys
22import os
23
24PY2 = sys.version_info[0] == 2
25PY3 = sys.version_info[0] == 3
26PY33 = PY3 and sys.version_info[1] >= 3
27
28
29# =============================================================================
30# Data types
31# =============================================================================
32if PY2:
33    # Python 2
34    TEXT_TYPES = (str, unicode)
35    INT_TYPES = (int, long)
36else:
37    # Python 3
38    TEXT_TYPES = (str,)
39    INT_TYPES = (int,)
40NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex])
41
42
43# =============================================================================
44# Renamed/Reorganized modules
45# =============================================================================
46if PY2:
47    # Python 2
48    import __builtin__ as builtins
49    from collections import Callable, MutableMapping
50    import ConfigParser as configparser
51    try:
52        import _winreg as winreg
53    except ImportError:
54        pass
55    from sys import maxint as maxsize
56    try:
57        import CStringIO as io
58    except ImportError:
59        import StringIO as io
60    try:
61        import cPickle as pickle
62    except ImportError:
63        import pickle
64    from UserDict import DictMixin as MutableMapping
65    import thread as _thread
66    import repr as reprlib
67else:
68    # Python 3
69    import builtins
70    import configparser
71    try:
72        import winreg
73    except ImportError:
74        pass
75    from sys import maxsize
76    import io
77    import pickle
78    if PY33:
79        from collections.abc import Callable, MutableMapping
80    else:
81        from collections import Callable, MutableMapping
82    import _thread
83    import reprlib
84
85
86# =============================================================================
87# Strings
88# =============================================================================
89if PY2:
90    # Python 2
91    import codecs
92
93    def u(obj):
94        """Make unicode object"""
95        return codecs.unicode_escape_decode(obj)[0]
96else:
97    # Python 3
98    def u(obj):
99        """Return string as it is"""
100        return obj
101
102
103def is_text_string(obj):
104    """Return True if `obj` is a text string, False if it is anything else,
105    like binary data (Python 3) or QString (Python 2, PyQt API #1)"""
106    if PY2:
107        # Python 2
108        return isinstance(obj, basestring)
109    else:
110        # Python 3
111        return isinstance(obj, str)
112
113
114def is_binary_string(obj):
115    """Return True if `obj` is a binary string, False if it is anything else"""
116    if PY2:
117        # Python 2
118        return isinstance(obj, str)
119    else:
120        # Python 3
121        return isinstance(obj, bytes)
122
123
124def is_string(obj):
125    """Return True if `obj` is a text or binary Python string object,
126    False if it is anything else, like a QString (Python 2, PyQt API #1)"""
127    return is_text_string(obj) or is_binary_string(obj)
128
129
130def is_unicode(obj):
131    """Return True if `obj` is unicode"""
132    if PY2:
133        # Python 2
134        return isinstance(obj, unicode)
135    else:
136        # Python 3
137        return isinstance(obj, str)
138
139
140def to_text_string(obj, encoding=None):
141    """Convert `obj` to (unicode) text string"""
142    if PY2:
143        # Python 2
144        if encoding is None:
145            return unicode(obj)
146        else:
147            return unicode(obj, encoding)
148    else:
149        # Python 3
150        if encoding is None:
151            return str(obj)
152        elif isinstance(obj, str):
153            # In case this function is not used properly, this could happen
154            return obj
155        else:
156            return str(obj, encoding)
157
158
159def to_binary_string(obj, encoding=None):
160    """Convert `obj` to binary string (bytes in Python 3, str in Python 2)"""
161    if PY2:
162        # Python 2
163        if encoding is None:
164            return str(obj)
165        else:
166            return obj.encode(encoding)
167    else:
168        # Python 3
169        return bytes(obj, 'utf-8' if encoding is None else encoding)
170
171
172# =============================================================================
173# Function attributes
174# =============================================================================
175def get_func_code(func):
176    """Return function code object"""
177    if PY2:
178        # Python 2
179        return func.func_code
180    else:
181        # Python 3
182        return func.__code__
183
184
185def get_func_name(func):
186    """Return function name"""
187    if PY2:
188        # Python 2
189        return func.func_name
190    else:
191        # Python 3
192        return func.__name__
193
194
195def get_func_defaults(func):
196    """Return function default argument values"""
197    if PY2:
198        # Python 2
199        return func.func_defaults
200    else:
201        # Python 3
202        return func.__defaults__
203
204
205# =============================================================================
206# Special method attributes
207# =============================================================================
208def get_meth_func(obj):
209    """Return method function object"""
210    if PY2:
211        # Python 2
212        return obj.im_func
213    else:
214        # Python 3
215        return obj.__func__
216
217
218def get_meth_class_inst(obj):
219    """Return method class instance"""
220    if PY2:
221        # Python 2
222        return obj.im_self
223    else:
224        # Python 3
225        return obj.__self__
226
227
228def get_meth_class(obj):
229    """Return method class"""
230    if PY2:
231        # Python 2
232        return obj.im_class
233    else:
234        # Python 3
235        return obj.__self__.__class__
236
237
238# =============================================================================
239# Misc.
240# =============================================================================
241if PY2:
242    # Python 2
243    input = raw_input
244    getcwd = os.getcwdu
245    cmp = cmp
246    import string
247    str_lower = string.lower
248    from itertools import izip_longest as zip_longest
249else:
250    # Python 3
251    input = input
252    getcwd = os.getcwd
253
254    def cmp(a, b):
255        return (a > b) - (a < b)
256    str_lower = str.lower
257    from itertools import zip_longest
258
259
260def qbytearray_to_str(qba):
261    """Convert QByteArray object to str in a way compatible with Python 2/3"""
262    return str(bytes(qba.toHex().data()).decode())
263