1# -*- coding: utf-8 -*-
2# -----------------------------------------------------------------------------
3# Copyright (c) 2009- Spyder Kernels Contributors
4#
5# Licensed under the terms of the MIT License
6# (see spyder_kernels/__init__.py for details)
7# -----------------------------------------------------------------------------
8
9"""
10spyder.py3compat
11----------------
12
13Transitional module providing compatibility functions intended to help
14migrating from Python 2 to Python 3.
15
16This module should be fully compatible with:
17    * Python >=v2.6
18    * Python 3
19"""
20
21from __future__ import print_function
22
23import operator
24import os
25import sys
26
27PY2 = sys.version[0] == '2'
28PY3 = sys.version[0] == '3'
29
30if PY3:
31    # keep reference to builtin_mod because the kernel overrides that value
32    # to forward requests to a frontend.
33    def input(prompt=''):
34        return builtin_mod.input(prompt)
35    builtin_mod_name = "builtins"
36    import builtins as builtin_mod
37else:
38    # keep reference to builtin_mod because the kernel overrides that value
39    # to forward requests to a frontend.
40    def input(prompt=''):
41        return builtin_mod.raw_input(prompt)
42    builtin_mod_name = "__builtin__"
43    import __builtin__ as builtin_mod
44
45
46#==============================================================================
47# Data types
48#==============================================================================
49if PY2:
50    # Python 2
51    TEXT_TYPES = (str, unicode)
52    INT_TYPES = (int, long)
53else:
54    # Python 3
55    TEXT_TYPES = (str,)
56    INT_TYPES = (int,)
57NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex])
58
59
60#==============================================================================
61# Renamed/Reorganized modules
62#==============================================================================
63if PY2:
64    # Python 2
65    import __builtin__ as builtins
66    import ConfigParser as configparser
67    try:
68        import _winreg as winreg
69    except ImportError:
70        pass
71    from sys import maxint as maxsize
72    try:
73        import CStringIO as io
74    except ImportError:
75        import StringIO as io
76    try:
77        import cPickle as pickle
78    except ImportError:
79        import pickle
80    from UserDict import DictMixin as MutableMapping
81    import thread as _thread
82    import repr as reprlib
83    import Queue
84else:
85    # Python 3
86    import builtins
87    import configparser
88    try:
89        import winreg
90    except ImportError:
91        pass
92    from sys import maxsize
93    import io
94    import pickle
95    from collections.abc import MutableMapping
96    import _thread
97    import reprlib
98    import queue as Queue
99
100
101#==============================================================================
102# Strings
103#==============================================================================
104def is_type_text_string(obj):
105    """Return True if `obj` is type text string, False if it is anything else,
106    like an instance of a class that extends the basestring class."""
107    if PY2:
108        # Python 2
109        return type(obj) in [str, unicode]
110    else:
111        # Python 3
112        return type(obj) in [str, bytes]
113
114def is_text_string(obj):
115    """Return True if `obj` is a text string, False if it is anything else,
116    like binary data (Python 3) or QString (Python 2, PyQt API #1)"""
117    if PY2:
118        # Python 2
119        return isinstance(obj, basestring)
120    else:
121        # Python 3
122        return isinstance(obj, str)
123
124def is_binary_string(obj):
125    """Return True if `obj` is a binary string, False if it is anything else"""
126    if PY2:
127        # Python 2
128        return isinstance(obj, str)
129    else:
130        # Python 3
131        return isinstance(obj, bytes)
132
133def is_string(obj):
134    """Return True if `obj` is a text or binary Python string object,
135    False if it is anything else, like a QString (Python 2, PyQt API #1)"""
136    return is_text_string(obj) or is_binary_string(obj)
137
138def is_unicode(obj):
139    """Return True if `obj` is unicode"""
140    if PY2:
141        # Python 2
142        return isinstance(obj, unicode)
143    else:
144        # Python 3
145        return isinstance(obj, str)
146
147def to_text_string(obj, encoding=None):
148    """Convert `obj` to (unicode) text string"""
149    if PY2:
150        # Python 2
151        if encoding is None:
152            return unicode(obj)
153        else:
154            return unicode(obj, encoding)
155    else:
156        # Python 3
157        if encoding is None:
158            return str(obj)
159        elif isinstance(obj, str):
160            # In case this function is not used properly, this could happen
161            return obj
162        else:
163            return str(obj, encoding)
164
165def to_binary_string(obj, encoding=None):
166    """Convert `obj` to binary string (bytes in Python 3, str in Python 2)"""
167    if PY2:
168        # Python 2
169        if encoding is None:
170            return str(obj)
171        else:
172            return obj.encode(encoding)
173    else:
174        # Python 3
175        return bytes(obj, 'utf-8' if encoding is None else encoding)
176
177
178#==============================================================================
179# Function attributes
180#==============================================================================
181def get_func_code(func):
182    """Return function code object"""
183    if PY2:
184        # Python 2
185        return func.func_code
186    else:
187        # Python 3
188        return func.__code__
189
190def get_func_name(func):
191    """Return function name"""
192    if PY2:
193        # Python 2
194        return func.func_name
195    else:
196        # Python 3
197        return func.__name__
198
199def get_func_defaults(func):
200    """Return function default argument values"""
201    if PY2:
202        # Python 2
203        return func.func_defaults
204    else:
205        # Python 3
206        return func.__defaults__
207
208
209#==============================================================================
210# Special method attributes
211#==============================================================================
212def get_meth_func(obj):
213    """Return method function object"""
214    if PY2:
215        # Python 2
216        return obj.im_func
217    else:
218        # Python 3
219        return obj.__func__
220
221def get_meth_class_inst(obj):
222    """Return method class instance"""
223    if PY2:
224        # Python 2
225        return obj.im_self
226    else:
227        # Python 3
228        return obj.__self__
229
230def get_meth_class(obj):
231    """Return method class"""
232    if PY2:
233        # Python 2
234        return obj.im_class
235    else:
236        # Python 3
237        return obj.__self__.__class__
238
239
240#==============================================================================
241# Misc.
242#==============================================================================
243if PY2:
244    def _print(*objects, **options):
245        end = options.get('end', '\n')
246        file = options.get('file', sys.stdout)
247        sep = options.get('sep', ' ')
248        string = sep.join([str(obj) for obj in objects])
249        print(string, file=file, end=end, sep=sep)
250else:
251    _print = print
252
253
254if PY2:
255    # Python 2
256    getcwd = os.getcwdu
257    cmp = cmp
258    import string
259    str_lower = string.lower
260    from itertools import izip_longest as zip_longest
261    from backports.functools_lru_cache import lru_cache
262else:
263    # Python 3
264    getcwd = os.getcwd
265    def cmp(a, b):
266        return (a > b) - (a < b)
267    str_lower = str.lower
268    from itertools import zip_longest
269    from functools import lru_cache
270
271def qbytearray_to_str(qba):
272    """Convert QByteArray object to str in a way compatible with Python 2/3"""
273    return str(bytes(qba.toHex().data()).decode())
274
275# =============================================================================
276# Dict funcs
277# =============================================================================
278if PY3:
279    def iterkeys(d, **kw):
280        return iter(d.keys(**kw))
281
282    def itervalues(d, **kw):
283        return iter(d.values(**kw))
284
285    def iteritems(d, **kw):
286        return iter(d.items(**kw))
287
288    def iterlists(d, **kw):
289        return iter(d.lists(**kw))
290
291    viewkeys = operator.methodcaller("keys")
292
293    viewvalues = operator.methodcaller("values")
294
295    viewitems = operator.methodcaller("items")
296else:
297    def iterkeys(d, **kw):
298        return d.iterkeys(**kw)
299
300    def itervalues(d, **kw):
301        return d.itervalues(**kw)
302
303    def iteritems(d, **kw):
304        return d.iteritems(**kw)
305
306    def iterlists(d, **kw):
307        return d.iterlists(**kw)
308
309    viewkeys = operator.methodcaller("viewkeys")
310
311    viewvalues = operator.methodcaller("viewvalues")
312
313    viewitems = operator.methodcaller("viewitems")
314
315# =============================================================================
316# Exceptions
317# =============================================================================
318if PY2:
319    TimeoutError = RuntimeError
320else:
321    TimeoutError = TimeoutError
322
323if PY2:
324    import re
325    import tokenize
326    def isidentifier(string):
327        """Check if string can be a variable name."""
328        return re.match(tokenize.Name + r'\Z', string) is not None
329
330    if os.name == 'nt':
331        def encode(u):
332            """Try encoding with utf8."""
333            if isinstance(u, unicode):
334                return u.encode('utf8', 'replace')
335            return u
336    else:
337        def encode(u):
338            """Try encoding with file system encoding."""
339            if isinstance(u, unicode):
340                return u.encode(sys.getfilesystemencoding())
341            return u
342else:
343    def isidentifier(string):
344        """Check if string can be a variable name."""
345        return string.isidentifier()
346
347    def encode(u):
348        """Encoding is not a problem in python 3."""
349        return u
350
351if __name__ == '__main__':
352    pass
353