# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE in the project root
# for license information.

from __future__ import absolute_import, division, print_function, unicode_literals

"""Python 2/3 compatibility helpers.
"""

import inspect
import itertools
import sys

from ptvsd.common import fmt


try:
    import builtins
except ImportError:
    import __builtin__ as builtins  # noqa

try:
    unicode = builtins.unicode
    bytes = builtins.str
except AttributeError:
    unicode = builtins.str
    bytes = builtins.bytes

try:
    xrange = builtins.xrange
except AttributeError:
    xrange = builtins.range

try:
    izip = itertools.izip
except AttributeError:
    izip = builtins.zip

try:
    reload = builtins.reload
except AttributeError:
    from importlib import reload  # noqa

try:
    import queue
except ImportError:
    import Queue as queue  # noqa


def force_unicode(s, encoding, errors="strict"):
    """Converts s to Unicode, using the provided encoding. If s is already Unicode,
    it is returned as is.
    """
    return s.decode(encoding, errors) if isinstance(s, bytes) else unicode(s)


def force_bytes(s, encoding, errors="strict"):
    """Converts s to bytes, using the provided encoding. If s is already bytes,
    it is returned as is.

    If errors="strict" and s is bytes, its encoding is verified by decoding it;
    UnicodeError is raised if it cannot be decoded.
    """
    if isinstance(s, unicode):
        return s.encode(encoding, errors)
    else:
        s = bytes(s)
        if errors == "strict":
            # Return value ignored - invoked solely for verification.
            s.decode(encoding, errors)
        return s


def force_str(s, encoding="ascii", errors="strict"):
    """Converts s to str (which is bytes on Python 2, and unicode on Python 3), using
    the provided encoding if necessary. If s is already str, it is returned as is.

    If errors="strict", str is bytes, and s is str, its encoding is verified by decoding
    it; UnicodeError is raised if it cannot be decoded.
    """
    return (force_bytes if str is bytes else force_unicode)(s, encoding, errors)


def force_ascii(s, errors="strict"):
    """Same as force_bytes(s, "ascii", errors)
    """
    return force_bytes(s, "ascii", errors)


def force_utf8(s, errors="strict"):
    """Same as force_bytes(s, "utf8", errors)
    """
    return force_bytes(s, "utf8", errors)


def filename(s, errors="strict"):
    """Same as force_unicode(s, sys.getfilesystemencoding(), errors)
    """
    return force_unicode(s, sys.getfilesystemencoding(), errors)


def filename_bytes(s, errors="strict"):
    """Same as force_bytes(s, sys.getfilesystemencoding(), errors)
    """
    return force_bytes(s, sys.getfilesystemencoding(), errors)


def filename_str(s, errors="strict"):
    """Same as force_str(s, sys.getfilesystemencoding(), errors)
    """
    return force_str(s, sys.getfilesystemencoding(), errors)


def nameof(obj, quote=False):
    """Returns the most descriptive name of a Python module, class, or function,
    as a Unicode string

    If quote=True, name is quoted with repr().

    Best-effort, but guaranteed to not fail - always returns something.
    """

    try:
        name = obj.__qualname__
    except Exception:
        try:
            name = obj.__name__
        except Exception:
            # Fall back to raw repr(), and skip quoting.
            try:
                name = repr(obj)
            except Exception:
                return "<unknown>"
            else:
                quote = False

    if quote:
        try:
            name = repr(name)
        except Exception:
            pass

    return force_unicode(name, "utf-8", "replace")


def unicode_repr(obj):
    """Like repr(), but guarantees that the result is Unicode even on Python 2.
    """
    return force_unicode(repr(obj), "ascii")


def srcnameof(obj):
    """Returns the most descriptive name of a Python module, class, or function,
    including source information (filename and linenumber), if available.

    Best-effort, but guaranteed to not fail - always returns something.
    """

    name = nameof(obj, quote=True)

    # Get the source information if possible.
    try:
        src_file = filename(inspect.getsourcefile(obj), "replace")
    except Exception:
        pass
    else:
        name += fmt(" (file {0!r}", src_file)
        try:
            _, src_lineno = inspect.getsourcelines(obj)
        except Exception:
            pass
        else:
            name += fmt(", line {0}", src_lineno)
        name += ")"

    return name

src/ptvsd/common/compat.py
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE in the project root
# for license information.

from __future__ import absolute_import, division, print_function, unicode_literals

"""Python 2/3 compatibility helpers.
"""

import inspect
import itertools
import sys

from ptvsd.common import fmt


try:
    import __builtin__ as builtins
except ImportError:
    import builtins

try:
    unicode = builtins.unicode
    bytes = builtins.str
except AttributeError:
    unicode = builtins.str
    bytes = builtins.bytes

try:
    xrange = builtins.xrange
except AttributeError:
    xrange = builtins.range

try:
    izip = itertools.izip
except AttributeError:
    izip = builtins.zip

try:
    reload = builtins.reload
except AttributeError:
    from importlib import reload  # noqa

try:
    import queue
except ImportError:
    import Queue as queue  # noqa


def force_unicode(s, encoding, errors="strict"):
    """Converts s to Unicode, using the provided encoding. If s is already Unicode,
    it is returned as is.
    """
    return s.decode(encoding, errors) if isinstance(s, bytes) else unicode(s)


def force_bytes(s, encoding, errors="strict"):
    """Converts s to bytes, using the provided encoding. If s is already bytes,
    it is returned as is.

    If errors="strict" and s is bytes, its encoding is verified by decoding it;
    UnicodeError is raised if it cannot be decoded.
    """
    if isinstance(s, unicode):
        return s.encode(encoding, errors)
    else:
        s = bytes(s)
        if errors == "strict":
            # Return value ignored - invoked solely for verification.
            s.decode(encoding, errors)
        return s


def force_str(s, encoding="ascii", errors="strict"):
    """Converts s to str (which is bytes on Python 2, and unicode on Python 3), using
    the provided encoding if necessary. If s is already str, it is returned as is.

    If errors="strict", str is bytes, and s is str, its encoding is verified by decoding
    it; UnicodeError is raised if it cannot be decoded.
    """
    return (force_bytes if str is bytes else force_unicode)(s, encoding, errors)


def force_ascii(s, errors="strict"):
    """Same as force_bytes(s, "ascii", errors)
    """
    return force_bytes(s, "ascii", errors)


def force_utf8(s, errors="strict"):
    """Same as force_bytes(s, "utf8", errors)
    """
    return force_bytes(s, "utf8", errors)


def filename(s, errors="strict"):
    """Same as force_unicode(s, sys.getfilesystemencoding(), errors)
    """
    return force_unicode(s, sys.getfilesystemencoding(), errors)


def filename_bytes(s, errors="strict"):
    """Same as force_bytes(s, sys.getfilesystemencoding(), errors)
    """
    return force_bytes(s, sys.getfilesystemencoding(), errors)


def filename_str(s, errors="strict"):
    """Same as force_str(s, sys.getfilesystemencoding(), errors)
    """
    return force_str(s, sys.getfilesystemencoding(), errors)


def nameof(obj, quote=False):
    """Returns the most descriptive name of a Python module, class, or function,
    as a Unicode string

    If quote=True, name is quoted with repr().

    Best-effort, but guaranteed to not fail - always returns something.
    """

    try:
        name = obj.__qualname__
    except Exception:
        try:
            name = obj.__name__
        except Exception:
            # Fall back to raw repr(), and skip quoting.
            try:
                name = repr(obj)
            except Exception:
                return "<unknown>"
            else:
                quote = False

    if quote:
        try:
            name = repr(name)
        except Exception:
            pass

    return force_unicode(name, "utf-8", "replace")


def unicode_repr(obj):
    """Like repr(), but guarantees that the result is Unicode even on Python 2.
    """
    return force_unicode(repr(obj), "ascii")


def srcnameof(obj):
    """Returns the most descriptive name of a Python module, class, or function,
    including source information (filename and linenumber), if available.

    Best-effort, but guaranteed to not fail - always returns something.
    """

    name = nameof(obj, quote=True)

    # Get the source information if possible.
    try:
        src_file = filename(inspect.getsourcefile(obj), "replace")
    except Exception:
        pass
    else:
        name += fmt(" (file {0!r}", src_file)
        try:
            _, src_lineno = inspect.getsourcelines(obj)
        except Exception:
            pass
        else:
            name += fmt(", line {0}", src_lineno)
        name += ")"

    return name

src/ptvsd/common/compat.py
diff --git a/src/ptvsd/common/compat.py b/src/ptvsd/common/compat.py
index ec0b61d29..d7cb2d7f2 100644
--- a/src/ptvsd/common/compat.py
+++ b/src/ptvsd/common/compat.py
@@ -15,9 +15,9 @@
 
 
 try:
-    import builtins
+    import __builtin__ as builtins
 except ImportError:
-    import __builtin__ as builtins  # noqa
+    import builtins
 
 try:
     unicode = builtins.unicode
diff --git a/tests/debug/session.py b/tests/debug/session.py
index 199184a8f..5f18af833 100644
--- a/tests/debug/session.py
+++ b/tests/debug/session.py
@@ -512,7 +512,7 @@ def _start_channel(self, stream):
         telemetry = self.wait_for_next_event("output")
         assert telemetry == {
             "category": "telemetry",
-            "output": "ptvsd.adapter",
+            "output": "ptvsd",
             "data": {"packageVersion": some.str},
         }
 

ptvsd broken on Python 2 if python-future is installed
Issue Type: <b>Bug</b>

After I installed an interpreter of Python 2.7.16 32bit, an exception is thrown with error message below when I want to switch to the old Python 2.7.16 64bit:
```
E+00000.045: /handling microsoft/vscode-python#1 request "launch" from Adapter-1/
             Handler 'launch_request' (file u'c:\\Users\\linshimeng01\\.vscode\\extensions\\ms-python.python-2019.11.50794\\pythonFiles\\lib\\python\\new_ptvsd\\no_wheels\\ptvsd\\launcher/../../ptvsd\\launcher\\adapter.py', line 35)
             couldn't handle microsoft/vscode-python#1 request "launch" from Adapter-1:

             Traceback (most recent call last):
               File "c:\Users\linshimeng01\.vscode\extensions\ms-python.python-2019.11.50794\pythonFiles\lib\python\new_ptvsd\no_wheels\ptvsd\launcher/../../ptvsd\common\messaging.py", line 763, in _handle
                 result = handler(self)
               File "c:\Users\linshimeng01\.vscode\extensions\ms-python.python-2019.11.50794\pythonFiles\lib\python\new_ptvsd\no_wheels\ptvsd\launcher/../../ptvsd\launcher\adapter.py", line 147, in launch_request
                 debuggee.spawn(process_name, cmdline, cwd, env, redirect_output)
               File "c:\Users\linshimeng01\.vscode\extensions\ms-python.python-2019.11.50794\pythonFiles\lib\python\new_ptvsd\no_wheels\ptvsd\launcher/../../ptvsd\launcher\debuggee.py", line 64, in spawn
                 "Couldn't spawn debuggee: {0}\n\nCommand line:{1!r}", exc, cmdline
             TypeError: unbound method cant_handle() must be called with Message instance as first argument (got unicode instance instead)

             Stack where logged:
               File "C:\Python27-32\lib\threading.py", line 774, in __bootstrap
                 self.__bootstrap_inner()
               File "C:\Python27-32\lib\threading.py", line 801, in __bootstrap_inner
                 self.run()
               File "C:\Python27-32\lib\threading.py", line 754, in run
                 self.__target(*self.__args, **self.__kwargs)
               File "c:\Users\linshimeng01\.vscode\extensions\ms-python.python-2019.11.50794\pythonFiles\lib\python\new_ptvsd\no_wheels\ptvsd\launcher/../../ptvsd\common\messaging.py", line 1520, in _run_handlers
                 handler()
               File "c:\Users\linshimeng01\.vscode\extensions\ms-python.python-2019.11.50794\pythonFiles\lib\python\new_ptvsd\no_wheels\ptvsd\launcher/../../ptvsd\common\messaging.py", line 809, in _handle
                 self.describe(),
```
Now I installed an Python 3 interpreter, and I found the two Python 2 interpreter are all unavailable and throw the same error message above as well when I attempt to run a python file. How can I fix it?

Extension version: 2019.11.50794
VS Code version: Code 1.40.1 (8795a9889db74563ddd43eb0a897a2384129a619, 2019-11-13T16:49:35.976Z)
OS version: Windows_NT x64 10.0.17763

<details>
<summary>System Info</summary>

|Item|Value|
|---|---|
|CPUs|Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz (6 x 2904)|
|GPU Status|2d_canvas: enabled<br>flash_3d: enabled<br>flash_stage3d: enabled<br>flash_stage3d_baseline: enabled<br>gpu_compositing: enabled<br>metal: disabled_off<br>multiple_raster_threads: enabled_on<br>oop_rasterization: disabled_off<br>protected_video_decode: unavailable_off<br>rasterization: enabled<br>skia_renderer: disabled_off<br>surface_control: disabled_off<br>surface_synchronization: enabled_on<br>video_decode: enabled<br>viz_display_compositor: enabled_on<br>viz_hit_test_surface_layer: disabled_off<br>webgl: enabled<br>webgl2: enabled|
|Load (avg)|undefined|
|Memory (System)|15.93GB (7.79GB free)|
|Process Argv||
|Screen Reader|no|
|VM|67%|
</details>
<!-- generated by issue reporter -->
