def test_as_client_default_host(self): addr = Address.as_client(None, 9786) self.assertEqual( addr, Address('localhost', 9786, isserver=False), )
def test_as_server_public_host(self): addr = Address.as_server('', 9786) self.assertEqual( addr, Address('', 9786, isserver=True), )
def test_new_wildcard_host(self): addr = Address('*', 9786) self.assertEqual( addr, Address('', 9786, isserver=True), )
def test_as_client_public_host(self): addr = Address.as_client('', 9786) self.assertEqual( addr, Address('', 9786, isserver=False), )
def test_as_server_default_host(self): addr = Address.as_server(None, 9786) self.assertEqual( addr, Address('localhost', 9786, isserver=True), )
def test_server(self): addr = Address.as_server('10.0.1.1', 8888) run_module(addr, 'spam', _run=self._run, _prog='eggs') self.assertEqual(self.argv, _get_args('--module', '--file', 'spam:')) self.assertEqual(self.addr, Address.as_server(*addr)) self.assertEqual(self.kwargs, {})
def test_as_client_valid_address(self): for host in ['localhost', '127.0.0.1', '::', '1.2.3.4']: with self.subTest(host): addr = Address.as_client(host, 9786) self.assertEqual( addr, Address(host, 9786, isserver=False), )
def test_nodebug_module_no_args(self): addr = Address.as_server('10.0.1.1', 8888) run_main(addr, 'spam.py', 'module', _runner=self._no_debug_runner) self.assertEqual(self.argv, ['spam.py']) self.assertEqual(self.addr, Address.as_server(*addr)) self.assertTrue(self.is_module) self.assertEqual(self.args, ()) self.assertEqual(self.kwargs, {})
def _parse_args(prog, argv): parser = argparse.ArgumentParser( prog=prog, usage=USAGE.format(prog), ) parser.add_argument('--nodebug', action='store_true') host = parser.add_mutually_exclusive_group() host.add_argument('--host') host.add_argument('--server-host') parser.add_argument('--port', type=int, required=True) target = parser.add_mutually_exclusive_group(required=True) target.add_argument('-m', dest='module') target.add_argument('--pid', type=int) target.add_argument('filename', nargs='?') parser.add_argument('--single-session', action='store_true') parser.add_argument('--wait', action='store_true') parser.add_argument('-V', '--version', action='version') parser.version = __version__ args = parser.parse_args(argv) ns = vars(args) serverhost = ns.pop('server_host', None) clienthost = ns.pop('host', None) if serverhost: args.address = Address.as_server(serverhost, ns.pop('port')) elif not clienthost: if args.nodebug: args.address = Address.as_client(clienthost, ns.pop('port')) else: args.address = Address.as_server(clienthost, ns.pop('port')) else: args.address = Address.as_client(clienthost, ns.pop('port')) pid = ns.pop('pid') module = ns.pop('module') filename = ns.pop('filename') if pid is not None: args.name = pid args.kind = 'pid' elif module is not None: args.name = module args.kind = 'module' else: args.name = filename args.kind = 'script' return args
def test_as_server_bad_port(self): port = None for host in [None, '', 'localhost', '1.2.3.4']: with self.subTest((host, port)): with self.assertRaises(TypeError): Address.as_server(host, port) for port in ['', -1, 65536]: for host in [None, '', 'localhost', '1.2.3.4']: with self.subTest((host, port)): with self.assertRaises(ValueError): Address.as_server(host, port)
def test_remote_localhost(self): addr = Address.as_client(None, 8888) run_module(addr, 'spam', _run=self._run, _prog='eggs') self.assertEqual(self.argv, _get_args( '--module', '--file', 'spam:', ptvsd_extras=['--client', 'localhost'])) self.assertEqual(self.addr, Address.as_client(*addr)) self.assertEqual(self.kwargs, { 'singlesession': True, })
def test_extra_sys_argv(self): filename = 'spam.py' port = 8888 debug_id = 1 debug_options = {'x': 'y'} sys.argv = [filename, '--abc', 'xyz', '42'] debug(filename, port, debug_id, debug_options, 'script', _run=self._run, _prog='eggs') self.assertEqual( self.argv, _get_args('--file', 'spam.py', '--abc', 'xyz', '42', ptvsd_extras=['--client', LOCALHOST])) self.assertEqual(self.addr, Address.as_client(None, port)) self.assertEqual(self.kwargs, { 'singlesession': True, })
def test_pseudo_backward_compatibility_nodebug(self): args, extra = parse_args([ 'eggs', '--nodebug', '--client', '--host', 'localhost', '--port', '8888', '--module', '--file', 'spam', ]) self.assertEqual( vars(args), { 'kind': 'script', 'name': 'spam', 'address': Address.as_client('localhost', 8888), 'nodebug': True, 'single_session': False, 'wait': False, 'multiprocess': False, }) self.assertEqual(extra, ['--module'] + self.EXPECTED_EXTRA)
def test_extra_nodebug(self): args, extra = parse_args([ 'eggs', '--DEBUG', '--nodebug', '--port', '8888', '--vm_type', '???', 'spam.py', '--xyz', '123', 'abc', '--cmd-line', '--', 'foo', '--server', '--bar' ]) self.assertEqual( vars(args), { 'kind': 'script', 'name': 'spam.py', 'address': Address.as_client(None, 8888), 'nodebug': True, 'single_session': False, 'wait': False, }) self.assertEqual( extra, [ '--DEBUG', '--vm_type', '???', '--', # Expected pydevd defaults separator '--xyz', '123', 'abc', '--cmd-line', 'foo', '--server', '--bar', ])
def install(pydevd, address, start_server=start_server, start_client=start_client, **kwargs): """Configure pydevd to use our wrapper. This is a bit of a hack to allow us to run our VSC debug adapter in the same process as pydevd. Note that, as with most hacks, this is somewhat fragile (since the monkeypatching sites may change). """ addr = Address.from_raw(address) daemon = Daemon(**kwargs) _start_server = (lambda p: start_server(daemon, addr.host, p)) _start_server.orig = start_server _start_client = (lambda h, p: start_client(daemon, h, p)) _start_client.orig = start_client # These are the functions pydevd invokes to get a socket to the client. pydevd_comm.start_server = _start_server pydevd_comm.start_client = _start_client # Ensure that pydevd is using our functions. pydevd.start_server = _start_server pydevd.start_client = _start_client __main__ = sys.modules['__main__'] if __main__ is not pydevd: if getattr(__main__, '__file__', None) == pydevd.__file__: __main__.start_server = _start_server __main__.start_client = _start_client return daemon
def test_attach_started_separately(self): lockfile = self.workspace.lockfile() done, waitscript = lockfile.wait_in_script() filename = self.write_script('spam.py', waitscript) addr = Address('localhost', 8888) with DebugAdapter.start_for_attach(addr, filename) as adapter: with DebugClient() as editor: session = editor.attach_socket(addr, adapter) with session.wait_for_event('thread'): (req_initialize, req_launch, req_config, _, _, _ ) = lifecycle_handshake(session, 'attach') done() adapter.wait() received = list(_strip_newline_output_events(session.received)) self.assert_received(received[:7], [ self.new_version_event(session.received), self.new_response(req_initialize.req, **INITIALIZE_RESPONSE), self.new_event('initialized'), self.new_response(req_launch.req), self.new_response(req_config.req), self.new_event('process', **{ 'isLocalProcess': True, 'systemProcessId': adapter.pid, 'startMethod': 'attach', 'name': filename, }), self.new_event('thread', reason='started', threadId=1), #self.new_event('thread', reason='exited', threadId=1), #self.new_event('exited', exitCode=0), #self.new_event('terminated'), ])
def test_wait_for_attach(self): addr = Address('localhost', PORT) filename = self.write_script( 'spam.py', """ import sys sys.path.insert(0, {!r}) import ptvsd ptvsd.enable_attach({}, redirect_output=False) ptvsd.wait_for_attach() # <ready> # <wait> """.format(PROJECT_ROOT, tuple(addr)), ) lockfile1 = self.workspace.lockfile() _, wait = set_release(filename, lockfile1, 'ready') lockfile2 = self.workspace.lockfile() done, _ = set_lock(filename, lockfile2, 'wait') adapter = DebugAdapter.start_embedded(addr, filename) with adapter: with DebugClient() as editor: session = editor.attach_socket(addr, adapter, timeout=1) # Ensure that it really does wait. with self.assertRaises(LockTimeoutError): wait(timeout=0.5) lifecycle_handshake(session, 'attach') wait(timeout=1) done() adapter.wait()
def start_for_attach(cls, addr, *args, **kwargs): srvtimeout = kwargs.pop('srvtimeout', SERVER_READY_TIMEOUT) addr = Address.as_server(*addr) adapter = cls._start_as(addr, *args, server=True, **kwargs) if srvtimeout is not None: wait_for_socket_server(addr, timeout=srvtimeout) return adapter
def start_client(self, addr): """Return ("socket", start_session) with a new client socket.""" ptvsd.log.debug('Starting client daemon on {0!r}.', addr) addr = Address.from_raw(addr) self._singlesession = True with self.started(): assert self.session is None client = create_client() connect(client, addr) sock = self._sock def start_session(**kwargs): self._check_ready_for_session() if self._server is not None: raise RuntimeError('running as server') if self._numsessions: raise RuntimeError('session stopped') try: self._bind_session(client) self._start_session_safely('ptvsd.Client', **kwargs) return self._session except Exception: self._stop_quietly() raise return sock, start_session
def run_file(address, filename, *extra, **kwargs): """Run pydevd for the given Python file.""" addr = Address.from_raw(address) run = kwargs.pop('_run', _run) prog = kwargs.pop('_prog', sys.argv[0]) argv = _run_argv(addr, filename, extra, _prog=prog) run(argv, addr, **kwargs)
def enable_attach(address, on_attach=(lambda: None), redirect_output=True, _pydevd=pydevd, _install=install, _settrace=_pydevd_settrace, **kwargs): addr = Address.as_server(*address) debug('installing ptvsd as server') # pydevd.settrace() forces a "client" connection, so we trick it # by setting start_client to start_server.. daemon = _install(_pydevd, addr, start_client=start_server, notify_session_debugger_ready=(lambda s: on_attach()), singlesession=False, **kwargs) def start_pydevd(): debug('enabling pydevd') # Only pass the port so start_server() gets triggered. # As noted above, we also have to trick settrace() because it # *always* forces a client connection. _settrace( stdoutToServer=redirect_output, stderrToServer=redirect_output, port=addr.port, suspend=False, _pydevd=_pydevd, ) debug('pydevd enabled') t = new_hidden_thread('start-pydevd', start_pydevd) t.start() def wait(timeout=None): t.join(timeout) return not t.is_alive() def debug_current_thread(suspend=False, **kwargs): # Make sure that pydevd has finished starting before enabling # in the current thread. t.join() debug('enabling pydevd (current thread)') _settrace( host=None, # ignored stdoutToServer=False, # ignored stderrToServer=False, # ignored port=None, # ignored suspend=suspend, trace_only_current_thread=True, overwrite_prev_trace=True, patch_multiprocessing=False, _pydevd=_pydevd, **kwargs) debug('pydevd enabled (current thread)') return daemon, wait, debug_current_thread
def run_file(address, filename, *extra, **kwargs): """Run pydevd for the given Python file.""" addr = Address.from_raw(address) if not addr.isserver: kwargs['singlesession'] = True run = kwargs.pop('_run', _run) prog = kwargs.pop('_prog', sys.argv[0]) argv = _run_argv(addr, filename, extra, _prog=prog) run(argv, addr, **kwargs)
def run_module(address, modname, *extra, **kwargs): """Run pydevd for the given module.""" addr = Address.from_raw(address) run = kwargs.pop('_run', _run) prog = kwargs.pop('_prog', sys.argv[0]) filename = modname + ':' argv = _run_argv(addr, filename, extra, _prog=prog) argv.insert(argv.index('--file'), '--module') run(argv, addr, **kwargs)
def test_local(self): addr = (None, 8888) run_file(addr, 'spam.py', _run=self._run, _prog='eggs') self.assertEqual(self.argv, _get_args('--file', 'spam.py')) self.assertEqual(self.addr, Address.as_server(*addr)) self.assertEqual(self.kwargs, {})
def test_executable(self): addr = (None, 8888) run_file(addr, 'spam.py', _run=self._run) self.assertEqual(self.argv, _get_args('--file', 'spam.py', prog=sys.argv[0])) self.assertEqual(self.addr, Address.as_server(*addr)) self.assertEqual(self.kwargs, {})
def test_attach_embedded(self): lockfile = self.workspace.lockfile() done, waitscript = lockfile.wait_in_script() addr = Address('localhost', 8888) script = dedent(""" from __future__ import print_function import sys sys.path.insert(0, {!r}) import ptvsd ptvsd.enable_attach({}, redirect_output={}) ptvsd.wait_for_attach() print('success!', end='') %s """).format(os.getcwd(), tuple(addr), True) filename = self.write_script('spam.py', script % waitscript) with DebugAdapter.start_embedded(addr, filename) as adapter: with DebugClient() as editor: session = editor.attach_socket(addr, adapter) (req_initialize, req_launch, req_config, _, _, _) = lifecycle_handshake(session, 'attach') Awaitable.wait_all(req_initialize, req_launch) done() adapter.wait() for i in range(10): # It could take some additional time for the adapter # to actually get the success output, so, wait for the # expected condition in a busy loop. out = adapter.output.decode('utf-8') if 'success!' in out: break import time time.sleep(.1) received = list(_strip_newline_output_events(session.received)) self.assert_contains(received, [ self.new_version_event(session.received), self.new_response(req_initialize.req, **INITIALIZE_RESPONSE), self.new_event('initialized'), self.new_response(req_launch.req), self.new_response(req_config.req), self.new_event( 'process', **{ 'isLocalProcess': True, 'systemProcessId': adapter.pid, 'startMethod': 'attach', 'name': filename, }), self.new_event('output', output='success!', category='stdout'), self.new_event('exited', exitCode=0), self.new_event('terminated'), ]) self.assertIn('success!', out)
def _parse_args(prog, argv): parser = argparse.ArgumentParser( prog=prog, usage=USAGE.format(prog), ) parser.add_argument('--nodebug', action='store_true') host = parser.add_mutually_exclusive_group() host.add_argument('--host') host.add_argument('--server-host') parser.add_argument('--port', type=int, required=True) target = parser.add_mutually_exclusive_group(required=True) target.add_argument('-m', dest='module') target.add_argument('filename', nargs='?') args = parser.parse_args(argv) ns = vars(args) serverhost = ns.pop('server_host', None) clienthost = ns.pop('host', None) if serverhost: args.address = Address.as_server(serverhost, ns.pop('port')) elif not clienthost: if args.nodebug: args.address = Address.as_client(clienthost, ns.pop('port')) else: args.address = Address.as_server(clienthost, ns.pop('port')) else: args.address = Address.as_client(clienthost, ns.pop('port')) module = ns.pop('module') filename = ns.pop('filename') if module is None: args.name = filename args.kind = 'script' else: args.name = module args.kind = 'module' #if argv[-1] != args.name or (module and argv[-1] != '-m'): # parser.error('script/module must be last arg') return args
def start_server(self, addr, hidebadsessions=True): """Return ("socket", next_session) with a new server socket.""" ptvsd.log.debug('Starting server daemon on {0!r}.', addr) addr = Address.from_raw(addr) with self.started(): assert self._sessionlock is None assert self.session is None self._server = create_server(addr.host, addr.port) host, port = self._server.getsockname() ptvsd.log.debug('Server socket created on {0!r}', addr) self._sessionlock = threading.Lock() sock = self._sock def check_ready(**kwargs): self._check_ready_for_session(**kwargs) if self._server is None: raise DaemonStoppedError() def next_session(timeout=None, **kwargs): server = self._server sessionlock = self._sessionlock check_ready(checksession=False) ptvsd.log.debug('Getting next session...') sessionlock.acquire() # Released in _finish_session(). ptvsd.log.debug('Session lock acquired.') # It may have closed or stopped while we waited. check_ready() timeout = kwargs.pop('timeout', None) try: ptvsd.log.debug('Getting session socket...') client = connect(server, None, **kwargs) self._bind_session(client) ptvsd.log.debug('Starting session...') self._start_session_safely('ptvsd.Server', timeout=timeout) ptvsd.log.debug('Session started.') return self._session except Exception: ptvsd.log.exception(category=('D' if hidebadsessions else 'E')) with ignore_errors(): self._finish_session() if hidebadsessions: ptvsd.log.debug('Hiding bad session') return None self._stop_quietly() raise if options.subprocess_notify: multiproc.notify_root(port) return sock, next_session
def run_module(address, modname, *extra, **kwargs): """Run pydevd for the given module.""" addr = Address.from_raw(address) if not addr.isserver: kwargs['singlesession'] = True run = kwargs.pop('_run', _run) prog = kwargs.pop('_prog', sys.argv[0]) filename = modname + ':' argv = _run_argv(addr, filename, extra, _prog=prog) argv.insert(argv.index('--file'), '--module') run(argv, addr, **kwargs)
def test_nodebug_module_args(self): addr = (None, 8888) args = ('--one', '--two', '--three') run_main(addr, 'spam.py', 'module', *args, _runner=self._no_debug_runner) self.assertEqual(self.argv, ['spam.py'] + list(args)) self.assertEqual(self.addr, Address.as_server(*addr)) self.assertTrue(self.is_module) self.assertEqual(self.args, args) self.assertEqual(self.kwargs, {})
def test_extra(self): addr = (None, 8888) run_module(addr, 'spam', '--vm_type', 'xyz', '--', '--DEBUG', _run=self._run, _prog='eggs') self.assertEqual(self.argv, _get_args( '--module', '--file', 'spam:', '--DEBUG', ptvsd_extras=['--vm_type', 'xyz'])) self.assertEqual(self.addr, Address.as_server(*addr)) self.assertEqual(self.kwargs, {})
def install(pydevd_module, address, start_server=start_server, start_client=start_client, **kwargs): """Configure pydevd to use our wrapper. This is a bit of a hack to allow us to run our VSC debug adapter in the same process as pydevd. Note that, as with most hacks, this is somewhat fragile (since the monkeypatching sites may change). """ ptvsd.log.debug('Installing pydevd hooks.') addr = Address.from_raw(address) daemon = Daemon(**kwargs) _start_server = (lambda p: start_server(daemon, addr.host, p)) _start_server.orig = start_server _start_client = (lambda h, p: start_client(daemon, h, p)) _start_client.orig = start_client # These are the functions pydevd invokes to get a socket to the client. pydevd_comm.start_server = _start_server pydevd_comm.start_client = _start_client # This is invoked when a child process is spawned with multiproc debugging enabled. pydev_monkey.patch_args = multiproc.patch_and_quote_args if not options.multiprocess and not options.no_debug: # This means '--multiprocess' flag was not passed via command line args. Patch the # new process functions here to handle multiprocess being enabled via debug options. ptvsd.log.debug('Monkey-patching multiprocess functions.') pydev_monkey.patch_new_process_functions() # Ensure that pydevd is using our functions. pydevd_module.start_server = _start_server pydevd_module.start_client = _start_client __main__ = sys.modules['__main__'] if __main__ is not pydevd: if getattr(__main__, '__file__', None) == pydevd.__file__: __main__.start_server = _start_server __main__.start_client = _start_client return daemon
def run_main(address, name, kind, *extra, **kwargs): addr = Address.from_raw(address) sys.argv[:] = _run_main_argv(name, extra) runner = kwargs.pop('_runner', no_debug_runner) runner(addr, name, kind == 'module', *extra, **kwargs)