1 # Copyright (c) Microsoft Corporation. All rights reserved. 2 # Licensed under the MIT License. See LICENSE in the project root 3 # for license information. 4 5 __all__ = ["main"] 6 7 import locale 8 import signal 9 import sys 10 11 # WARNING: debugpy and submodules must not be imported on top level in this module, 12 # and should be imported locally inside main() instead. 13 14 15 def main(): 16 from debugpy import launcher 17 from debugpy.common import log 18 from debugpy.launcher import debuggee 19 20 log.to_file(prefix="debugpy.launcher") 21 log.describe_environment("debugpy.launcher startup environment:") 22 23 if sys.platform == "win32": 24 # For windows, disable exceptions on Ctrl+C - we want to allow the debuggee 25 # process to handle these, or not, as it sees fit. If the debuggee exits 26 # on Ctrl+C, the launcher will also exit, so it doesn't need to observe 27 # the signal directly. 28 signal.signal(signal.SIGINT, signal.SIG_IGN) 29 30 # Everything before "--" is command line arguments for the launcher itself, 31 # and everything after "--" is command line arguments for the debuggee. 32 log.info("sys.argv before parsing: {0}", sys.argv) 33 sep = sys.argv.index("--") 34 launcher_argv = sys.argv[1:sep] 35 sys.argv[:] = [sys.argv[0]] + sys.argv[sep + 1 :] 36 log.info("sys.argv after patching: {0}", sys.argv) 37 38 # The first argument specifies the host/port on which the adapter is waiting 39 # for launcher to connect. It's either host:port, or just port. 40 adapter = launcher_argv[0] 41 host, sep, port = adapter.partition(":") 42 if not sep: 43 host = "127.0.0.1" 44 port = adapter 45 port = int(port) 46 47 launcher.connect(host, port) 48 launcher.channel.wait() 49 50 if debuggee.process is not None: 51 sys.exit(debuggee.process.returncode) 52 53 54 if __name__ == "__main__": 55 # debugpy can also be invoked directly rather than via -m. In this case, the first 56 # entry on sys.path is the one added automatically by Python for the directory 57 # containing this file. This means that import debugpy will not work, since we need 58 # the parent directory of debugpy/ to be in sys.path, rather than debugpy/launcher/. 59 # 60 # The other issue is that many other absolute imports will break, because they 61 # will be resolved relative to debugpy/launcher/ - e.g. `import state` will then try 62 # to import debugpy/launcher/state.py. 63 # 64 # To fix both, we need to replace the automatically added entry such that it points 65 # at parent directory of debugpy/ instead of debugpy/launcher, import debugpy with that 66 # in sys.path, and then remove the first entry entry altogether, so that it doesn't 67 # affect any further imports we might do. For example, suppose the user did: 68 # 69 # python /foo/bar/debugpy/launcher ... 70 # 71 # At the beginning of this script, sys.path will contain "/foo/bar/debugpy/launcher" 72 # as the first entry. What we want is to replace it with "/foo/bar', then import 73 # debugpy with that in effect, and then remove the replaced entry before any more 74 # code runs. The imported debugpy module will remain in sys.modules, and thus all 75 # future imports of it or its submodules will resolve accordingly. 76 if "debugpy" not in sys.modules: 77 # Do not use dirname() to walk up - this can be a relative path, e.g. ".". 78 sys.path[0] = sys.path[0] + "/../../" 79 __import__("debugpy") 80 del sys.path[0] 81 82 # Apply OS-global and user-specific locale settings. 83 try: 84 locale.setlocale(locale.LC_ALL, "") 85 except Exception: 86 # On POSIX, locale is set via environment variables, and this can fail if 87 # those variables reference a non-existing locale. Ignore and continue using 88 # the default "C" locale if so. 89 pass 90 91 main() 92