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    import ConfigParser as configparser
50    try:
51        import _winreg as winreg
52    except ImportError:
53        pass
54    from sys import maxint as maxsize
55    try:
56        import CStringIO as io
57    except ImportError:
58        import StringIO as io
59    try:
60        import cPickle as pickle
61    except ImportError:
62        import pickle
63    from UserDict import DictMixin as MutableMapping
64    import thread as _thread
65    import repr as reprlib
66else:
67    # Python 3
68    import builtins
69    import configparser
70    try:
71        import winreg
72    except ImportError:
73        pass
74    from sys import maxsize
75    import io
76    import pickle
77    if PY33:
78        from collections.abc import MutableMapping
79    else:
80        from collections import MutableMapping
81    import _thread
82    import reprlib
83
84
85# =============================================================================
86# Strings
87# =============================================================================
88if PY2:
89    # Python 2
90    import codecs
91
92    def u(obj):
93        """Make unicode object"""
94        return codecs.unicode_escape_decode(obj)[0]
95else:
96    # Python 3
97    def u(obj):
98        """Return string as it is"""
99        return obj
100
101
102def is_text_string(obj):
103    """Return True if `obj` is a text string, False if it is anything else,
104    like binary data (Python 3) or QString (Python 2, PyQt API #1)"""
105    if PY2:
106        # Python 2
107        return isinstance(obj, basestring)
108    else:
109        # Python 3
110        return isinstance(obj, str)
111
112
113def is_binary_string(obj):
114    """Return True if `obj` is a binary string, False if it is anything else"""
115    if PY2:
116        # Python 2
117        return isinstance(obj, str)
118    else:
119        # Python 3
120        return isinstance(obj, bytes)
121
122
123def is_string(obj):
124    """Return True if `obj` is a text or binary Python string object,
125    False if it is anything else, like a QString (Python 2, PyQt API #1)"""
126    return is_text_string(obj) or is_binary_string(obj)
127
128
129def is_unicode(obj):
130    """Return True if `obj` is unicode"""
131    if PY2:
132        # Python 2
133        return isinstance(obj, unicode)
134    else:
135        # Python 3
136        return isinstance(obj, str)
137
138
139def to_text_string(obj, encoding=None):
140    """Convert `obj` to (unicode) text string"""
141    if PY2:
142        # Python 2
143        if encoding is None:
144            return unicode(obj)
145        else:
146            return unicode(obj, encoding)
147    else:
148        # Python 3
149        if encoding is None:
150            return str(obj)
151        elif isinstance(obj, str):
152            # In case this function is not used properly, this could happen
153            return obj
154        else:
155            return str(obj, encoding)
156
157
158def to_binary_string(obj, encoding=None):
159    """Convert `obj` to binary string (bytes in Python 3, str in Python 2)"""
160    if PY2:
161        # Python 2
162        if encoding is None:
163            return str(obj)
164        else:
165            return obj.encode(encoding)
166    else:
167        # Python 3
168        return bytes(obj, 'utf-8' if encoding is None else encoding)
169
170
171# =============================================================================
172# Function attributes
173# =============================================================================
174def get_func_code(func):
175    """Return function code object"""
176    if PY2:
177        # Python 2
178        return func.func_code
179    else:
180        # Python 3
181        return func.__code__
182
183
184def get_func_name(func):
185    """Return function name"""
186    if PY2:
187        # Python 2
188        return func.func_name
189    else:
190        # Python 3
191        return func.__name__
192
193
194def get_func_defaults(func):
195    """Return function default argument values"""
196    if PY2:
197        # Python 2
198        return func.func_defaults
199    else:
200        # Python 3
201        return func.__defaults__
202
203
204# =============================================================================
205# Special method attributes
206# =============================================================================
207def get_meth_func(obj):
208    """Return method function object"""
209    if PY2:
210        # Python 2
211        return obj.im_func
212    else:
213        # Python 3
214        return obj.__func__
215
216
217def get_meth_class_inst(obj):
218    """Return method class instance"""
219    if PY2:
220        # Python 2
221        return obj.im_self
222    else:
223        # Python 3
224        return obj.__self__
225
226
227def get_meth_class(obj):
228    """Return method class"""
229    if PY2:
230        # Python 2
231        return obj.im_class
232    else:
233        # Python 3
234        return obj.__self__.__class__
235
236
237# =============================================================================
238# Misc.
239# =============================================================================
240if PY2:
241    # Python 2
242    input = raw_input
243    getcwd = os.getcwdu
244    cmp = cmp
245    import string
246    str_lower = string.lower
247    from itertools import izip_longest as zip_longest
248else:
249    # Python 3
250    input = input
251    getcwd = os.getcwd
252
253    def cmp(a, b):
254        return (a > b) - (a < b)
255    str_lower = str.lower
256    from itertools import zip_longest
257
258
259def qbytearray_to_str(qba):
260    """Convert QByteArray object to str in a way compatible with Python 2/3"""
261    return str(bytes(qba.toHex().data()).decode())
262