1# -*- coding: utf-8 -*-
2#
3# Copyright © Spyder Project Contributors
4# Licensed under the terms of the MIT License
5# (see spyder/__init__.py for details)
6
7"""
8spyder.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 operator
22import os
23import sys
24
25PY2 = sys.version[0] == '2'
26PY3 = sys.version[0] == '3'
27
28#==============================================================================
29# Data types
30#==============================================================================
31if PY2:
32    # Python 2
33    TEXT_TYPES = (str, unicode)
34    INT_TYPES = (int, long)
35else:
36    # Python 3
37    TEXT_TYPES = (str,)
38    INT_TYPES = (int,)
39NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex])
40
41
42#==============================================================================
43# Renamed/Reorganized modules
44#==============================================================================
45if PY2:
46    # Python 2
47    import __builtin__ as builtins
48    import ConfigParser as configparser
49    try:
50        import _winreg as winreg
51    except ImportError:
52        pass
53    from sys import maxint as maxsize
54    try:
55        import CStringIO as io
56    except ImportError:
57        import StringIO as io
58    try:
59        import cPickle as pickle
60    except ImportError:
61        import pickle
62    from UserDict import DictMixin as MutableMapping
63    import thread as _thread
64    import repr as reprlib
65    import Queue
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    from collections import MutableMapping
78    import _thread
79    import reprlib
80    import queue as Queue
81
82
83#==============================================================================
84# Strings
85#==============================================================================
86def is_type_text_string(obj):
87    """Return True if `obj` is type text string, False if it is anything else,
88    like an instance of a class that extends the basestring class."""
89    if PY2:
90        # Python 2
91        return type(obj) in [str, unicode]
92    else:
93        # Python 3
94        return type(obj) in [str, bytes]
95
96def is_text_string(obj):
97    """Return True if `obj` is a text string, False if it is anything else,
98    like binary data (Python 3) or QString (Python 2, PyQt API #1)"""
99    if PY2:
100        # Python 2
101        return isinstance(obj, basestring)
102    else:
103        # Python 3
104        return isinstance(obj, str)
105
106def is_binary_string(obj):
107    """Return True if `obj` is a binary string, False if it is anything else"""
108    if PY2:
109        # Python 2
110        return isinstance(obj, str)
111    else:
112        # Python 3
113        return isinstance(obj, bytes)
114
115def is_string(obj):
116    """Return True if `obj` is a text or binary Python string object,
117    False if it is anything else, like a QString (Python 2, PyQt API #1)"""
118    return is_text_string(obj) or is_binary_string(obj)
119
120def is_unicode(obj):
121    """Return True if `obj` is unicode"""
122    if PY2:
123        # Python 2
124        return isinstance(obj, unicode)
125    else:
126        # Python 3
127        return isinstance(obj, str)
128
129def to_text_string(obj, encoding=None):
130    """Convert `obj` to (unicode) text string"""
131    if PY2:
132        # Python 2
133        if encoding is None:
134            return unicode(obj)
135        else:
136            return unicode(obj, encoding)
137    else:
138        # Python 3
139        if encoding is None:
140            return str(obj)
141        elif isinstance(obj, str):
142            # In case this function is not used properly, this could happen
143            return obj
144        else:
145            return str(obj, encoding)
146
147def to_binary_string(obj, encoding=None):
148    """Convert `obj` to binary string (bytes in Python 3, str in Python 2)"""
149    if PY2:
150        # Python 2
151        if encoding is None:
152            return str(obj)
153        else:
154            return obj.encode(encoding)
155    else:
156        # Python 3
157        return bytes(obj, 'utf-8' if encoding is None else encoding)
158
159
160#==============================================================================
161# Function attributes
162#==============================================================================
163def get_func_code(func):
164    """Return function code object"""
165    if PY2:
166        # Python 2
167        return func.func_code
168    else:
169        # Python 3
170        return func.__code__
171
172def get_func_name(func):
173    """Return function name"""
174    if PY2:
175        # Python 2
176        return func.func_name
177    else:
178        # Python 3
179        return func.__name__
180
181def get_func_defaults(func):
182    """Return function default argument values"""
183    if PY2:
184        # Python 2
185        return func.func_defaults
186    else:
187        # Python 3
188        return func.__defaults__
189
190
191#==============================================================================
192# Special method attributes
193#==============================================================================
194def get_meth_func(obj):
195    """Return method function object"""
196    if PY2:
197        # Python 2
198        return obj.im_func
199    else:
200        # Python 3
201        return obj.__func__
202
203def get_meth_class_inst(obj):
204    """Return method class instance"""
205    if PY2:
206        # Python 2
207        return obj.im_self
208    else:
209        # Python 3
210        return obj.__self__
211
212def get_meth_class(obj):
213    """Return method class"""
214    if PY2:
215        # Python 2
216        return obj.im_class
217    else:
218        # Python 3
219        return obj.__self__.__class__
220
221
222#==============================================================================
223# Misc.
224#==============================================================================
225if PY2:
226    # Python 2
227    input = raw_input
228    getcwd = os.getcwdu
229    cmp = cmp
230    import string
231    str_lower = string.lower
232    from itertools import izip_longest as zip_longest
233else:
234    # Python 3
235    input = input
236    getcwd = os.getcwd
237    def cmp(a, b):
238        return (a > b) - (a < b)
239    str_lower = str.lower
240    from itertools import zip_longest
241
242def qbytearray_to_str(qba):
243    """Convert QByteArray object to str in a way compatible with Python 2/3"""
244    return str(bytes(qba.toHex().data()).decode())
245
246# =============================================================================
247# Dict funcs
248# =============================================================================
249if PY3:
250    def iterkeys(d, **kw):
251        return iter(d.keys(**kw))
252
253    def itervalues(d, **kw):
254        return iter(d.values(**kw))
255
256    def iteritems(d, **kw):
257        return iter(d.items(**kw))
258
259    def iterlists(d, **kw):
260        return iter(d.lists(**kw))
261
262    viewkeys = operator.methodcaller("keys")
263
264    viewvalues = operator.methodcaller("values")
265
266    viewitems = operator.methodcaller("items")
267else:
268    def iterkeys(d, **kw):
269        return d.iterkeys(**kw)
270
271    def itervalues(d, **kw):
272        return d.itervalues(**kw)
273
274    def iteritems(d, **kw):
275        return d.iteritems(**kw)
276
277    def iterlists(d, **kw):
278        return d.iterlists(**kw)
279
280    viewkeys = operator.methodcaller("viewkeys")
281
282    viewvalues = operator.methodcaller("viewvalues")
283
284    viewitems = operator.methodcaller("viewitems")
285
286
287if __name__ == '__main__':
288    pass
289