Repository: microsoft/ptvsd

Issue:
Unable to launch the debugger
Getting the following error in master when debugging in VSC:
```
Could not connect to None: 60857
Traceback (most recent call last):
  File "/Users/donjayamanne/Desktop/Development/vscode/ptvsd/ptvsd/pydevd/pydevd.py", line 1620, in main
    debugger.connect(host, port)
  File "/Users/donjayamanne/Desktop/Development/vscode/ptvsd/ptvsd/pydevd/pydevd.py", line 326, in connect
    s = start_server(port)
  File "/Users/donjayamanne/Desktop/Development/vscode/ptvsd/ptvsd/wrapper.py", line 1766, in start_server
    server = _create_server(port)
  File "/Users/donjayamanne/Desktop/Development/vscode/ptvsd/ptvsd/wrapper.py", line 1701, in _create_server
    server.bind(('127.0.0.1', port))
OSError: [Errno 48] Address already in u
```


Discussion:
This would indicate the live tests we have today are not sufficient or not mimicking VSC.
@karthiknadig Please could you test the latest master branch as well.
@ericsnowcurrently 

The problem is the `--client` arg is not appended into the list when it should be.

```python
def _run_argv(address, filename, *extra):
    """Convert the given values to an argv that pydevd.main() supports."""
    if '--' in extra:
        pydevd = list(extra[:extra.index('--')])
        extra = list(extra[len(pydevd) + 1:])
    else:
        pydevd = []
        extra = list(extra)

    host, port=address
    print(host)
    #if host is None:
    #    host = '127.0.0.1'
    argv = [
        sys.argv[0],
        '--port', str(port),
    ]
    if host is not None:
        argv.extend([
            '--client', host,
        ])
```

Today I'm launching PTVSD by invoking the function `debug` in `debugger.py`:
```typescript
vspd.debug(filename, port_num, debug_id, debug_options, run_as)
```
@ericsnowcurrently 
When possible, please do look at this issue. Should be simple. I'd fix it, but I'm not sure what the right approach is, as you have commented out the code that initializes the host to `127.0.0.1`.

Oh yes, this PR is quite crucial for VSC, as it breaks all of our PRs (we're pulling the master branch of PTVSD in VSC CI tests , i.e. none of our CI tests pass as a result of this 😄)
FWIW, I expect that the culprit is PR #266.
@ericsnowcurrently Assigned two other issues to you (I believe they are related).

Resolving patch:
diff --git a/ptvsd/__main__.py b/ptvsd/__main__.py
--- a/ptvsd/__main__.py
+++ b/ptvsd/__main__.py
@@ -17,19 +17,23 @@
 
 def run_module(address, modname, *extra, **kwargs):
     """Run pydevd for the given module."""
+    run = kwargs.pop('_run', _run)
+    prog = kwargs.pop('_prog', sys.argv[0])
     filename = modname + ':'
-    argv = _run_argv(address, filename, *extra)
+    argv = _run_argv(address, filename, extra, _prog=prog)
     argv.insert(argv.index('--file'), '--module')
-    _run(argv, **kwargs)
+    run(argv, **kwargs)
 
 
 def run_file(address, filename, *extra, **kwargs):
     """Run pydevd for the given Python file."""
-    argv = _run_argv(address, filename, *extra)
-    _run(argv, **kwargs)
+    run = kwargs.pop('_run', _run)
+    prog = kwargs.pop('_prog', sys.argv[0])
+    argv = _run_argv(address, filename, extra, _prog=prog)
+    run(argv, **kwargs)
 
 
-def _run_argv(address, filename, *extra):
+def _run_argv(address, filename, extra, _prog=sys.argv[0]):
     """Convert the given values to an argv that pydevd.main() supports."""
     if '--' in extra:
         pydevd = list(extra[:extra.index('--')])
@@ -42,7 +46,7 @@ def _run_argv(address, filename, *extra):
     #if host is None:
     #    host = '127.0.0.1'
     argv = [
-        sys.argv[0],
+        _prog,
         '--port', str(port),
     ]
     if host is not None:
@@ -54,7 +58,7 @@ def _run_argv(address, filename, *extra):
     ] + extra
 
 
-def _run(argv, **kwargs):
+def _run(argv, _pydevd=pydevd, _install=ptvsd.wrapper.install, **kwargs):
     """Start pydevd with the given commandline args."""
     #print(' '.join(argv))
 
@@ -72,14 +76,14 @@ def _run(argv, **kwargs):
     # imports of the "pydevd" module then return the wrong module.  We
     # work around this by avoiding lazy imports of the "pydevd" module.
     # We also replace the __main__ module with the "pydevd" module here.
-    if sys.modules['__main__'].__file__ != pydevd.__file__:
+    if sys.modules['__main__'].__file__ != _pydevd.__file__:
         sys.modules['__main___orig'] = sys.modules['__main__']
-        sys.modules['__main__'] = pydevd
+        sys.modules['__main__'] = _pydevd
 
-    ptvsd.wrapper.install(pydevd, **kwargs)
+    _install(_pydevd, **kwargs)
     sys.argv[:] = argv
     try:
-        pydevd.main()
+        _pydevd.main()
     except SystemExit as ex:
         ptvsd.wrapper.ptvsd_sys_exit_code = int(ex.code)
         raise
@@ -135,7 +139,10 @@ def parse_args(argv=None):
 
     supported, pydevd, script = _group_args(argv)
     args = _parse_args(prog, supported)
-    return args, pydevd + ['--'] + script
+    extra = pydevd
+    if script:
+        extra += ['--'] + script
+    return args, extra
 
 
 def _group_args(argv):
@@ -176,9 +183,9 @@ def _group_args(argv):
         if arg == '--client':
             arg = '--host'
         elif arg == '--file':
-            if nextarg is None:
+            if nextarg is None:  # The filename is missing...
                 pydevd.append(arg)
-                continue
+                continue  # This will get handled later.
             if nextarg.endswith(':') and '--module' in pydevd:
                 pydevd.remove('--module')
                 arg = '-m'
diff --git a/ptvsd/debugger.py b/ptvsd/debugger.py
--- a/ptvsd/debugger.py
+++ b/ptvsd/debugger.py
@@ -2,6 +2,8 @@
 # Licensed under the MIT License. See LICENSE in the project root
 # for license information.
 
+import sys
+
 from ptvsd.__main__ import run_module, run_file
 
 
@@ -11,11 +13,27 @@
 # TODO: not needed?
 DONT_DEBUG = []
 
+LOCALHOST = 'localhost'
+
+RUNNERS = {
+    'module': run_module,  # python -m spam
+    'script': run_file,  # python spam.py
+    'code': run_file,  # python -c 'print("spam")'
+    None: run_file,  # catchall
+}
+
 
-def debug(filename, port_num, debug_id, debug_options, run_as, **kwargs):
+def debug(filename, port_num, debug_id, debug_options, run_as,
+          _runners=RUNNERS, _extra=None, *args, **kwargs):
     # TODO: docstring
-    address = ('localhost', port_num)
-    if run_as == 'module':
-        run_module(address, filename, **kwargs)
-    else:
-        run_file(address, filename, **kwargs)
+    if _extra is None:
+        _extra = sys.argv[1:]
+    address = (LOCALHOST, port_num)
+    try:
+        run = _runners[run_as]
+    except KeyError:
+        # TODO: fail?
+        run = _runners[None]
+    if _extra:
+        args = _extra + list(args)
+    run(address, filename, *args, **kwargs)


Test patch:
diff --git a/tests/ptvsd/test___main__.py b/tests/ptvsd/test___main__.py
new file mode 100644
--- /dev/null
+++ b/tests/ptvsd/test___main__.py
@@ -0,0 +1,412 @@
+import contextlib
+from io import StringIO
+import sys
+import unittest
+
+from _pydevd_bundle import pydevd_comm
+
+import ptvsd.wrapper
+from ptvsd.__main__ import run_module, run_file, parse_args
+
+if sys.version_info < (3,):
+    from io import BytesIO as StringIO  # noqa
+
+
+@contextlib.contextmanager
+def captured_stdio(out=None, err=None):
+    if out is None:
+        if err is None:
+            out = err = StringIO()
+        elif err is False:
+            out = StringIO()
+    elif err is None and out is False:
+        err = StringIO()
+    if out is False:
+        out = None
+    if err is False:
+        err = None
+
+    orig = sys.stdout, sys.stderr
+    if out is not None:
+        sys.stdout = out
+    if err is not None:
+        sys.stderr = err
+    try:
+        yield out, err
+    finally:
+        sys.stdout, sys.stderr = orig
+
+
+class FakePyDevd(object):
+
+    def __init__(self, __file__, handle_main):
+        self.__file__ = __file__
+        self.handle_main = handle_main
+
+    def main(self):
+        self.handle_main()
+
+
+class RunBase(object):
+
+    def setUp(self):
+        super(RunBase, self).setUp()
+        self.argv = None
+        self.kwargs = None
+
+    def _run(self, argv, **kwargs):
+        self.argv = argv
+        self.kwargs = kwargs
+
+
+class RunModuleTests(RunBase, unittest.TestCase):
+
+    def test_local(self):
+        addr = (None, 8888)
+        run_module(addr, 'spam', _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--module',
+            '--file', 'spam:',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_remote(self):
+        addr = ('1.2.3.4', 8888)
+        run_module(addr, 'spam', _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--client', '1.2.3.4',
+            '--module',
+            '--file', 'spam:',
+        ])
+        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, [
+            'eggs',
+            '--port', '8888',
+            '--vm_type', 'xyz',
+            '--module',
+            '--file', 'spam:',
+            '--DEBUG',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_executable(self):
+        addr = (None, 8888)
+        run_module(addr, 'spam', _run=self._run)
+
+        self.assertEqual(self.argv, [
+            sys.argv[0],
+            '--port', '8888',
+            '--module',
+            '--file', 'spam:',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+
+class RunScriptTests(RunBase, unittest.TestCase):
+
+    def test_local(self):
+        addr = (None, 8888)
+        run_file(addr, 'spam.py', _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--file', 'spam.py',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_remote(self):
+        addr = ('1.2.3.4', 8888)
+        run_file(addr, 'spam.py', _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--client', '1.2.3.4',
+            '--file', 'spam.py',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_extra(self):
+        addr = (None, 8888)
+        run_file(addr, 'spam.py', '--vm_type', 'xyz', '--', '--DEBUG',
+                 _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--vm_type', 'xyz',
+            '--file', 'spam.py',
+            '--DEBUG',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_executable(self):
+        addr = (None, 8888)
+        run_file(addr, 'spam.py', _run=self._run)
+
+        self.assertEqual(self.argv, [
+            sys.argv[0],
+            '--port', '8888',
+            '--file', 'spam.py',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+
+class IntegratedRunTests(unittest.TestCase):
+
+    def setUp(self):
+        super(IntegratedRunTests, self).setUp()
+        self.__main__ = sys.modules['__main__']
+        self.argv = sys.argv
+        ptvsd.wrapper.ptvsd_sys_exit_code = 0
+        self.start_server = pydevd_comm.start_server
+        self.start_client = pydevd_comm.start_client
+
+        self.pydevd = None
+        self.kwargs = None
+        self.maincalls = 0
+        self.mainexc = None
+
+    def tearDown(self):
+        sys.argv[:] = self.argv
+        sys.modules['__main__'] = self.__main__
+        sys.modules.pop('__main___orig', None)
+        ptvsd.wrapper.ptvsd_sys_exit_code = 0
+        pydevd_comm.start_server = self.start_server
+        pydevd_comm.start_client = self.start_client
+        # We shouldn't need to restore __main__.start_*.
+        super(IntegratedRunTests, self).tearDown()
+
+    def _install(self, pydevd, **kwargs):
+        self.pydevd = pydevd
+        self.kwargs = kwargs
+
+    def _main(self):
+        self.maincalls += 1
+        if self.mainexc is not None:
+            raise self.mainexc
+
+    def test_run(self):
+        pydevd = FakePyDevd('pydevd/pydevd.py', self._main)
+        addr = (None, 8888)
+        run_file(addr, 'spam.py', _pydevd=pydevd, _install=self._install)
+
+        self.assertEqual(self.pydevd, pydevd)
+        self.assertEqual(self.kwargs, {})
+        self.assertEqual(self.maincalls, 1)
+        self.assertEqual(sys.argv, [
+            sys.argv[0],
+            '--port', '8888',
+            '--file', 'spam.py',
+        ])
+        self.assertEqual(ptvsd.wrapper.ptvsd_sys_exit_code, 0)
+
+    def test_failure(self):
+        self.mainexc = RuntimeError('boom!')
+        pydevd = FakePyDevd('pydevd/pydevd.py', self._main)
+        addr = (None, 8888)
+        with self.assertRaises(RuntimeError) as cm:
+            run_file(addr, 'spam.py', _pydevd=pydevd, _install=self._install)
+        exc = cm.exception
+
+        self.assertEqual(self.pydevd, pydevd)
+        self.assertEqual(self.kwargs, {})
+        self.assertEqual(self.maincalls, 1)
+        self.assertEqual(sys.argv, [
+            sys.argv[0],
+            '--port', '8888',
+            '--file', 'spam.py',
+        ])
+        self.assertEqual(ptvsd.wrapper.ptvsd_sys_exit_code, 0)
+        self.assertIs(exc, self.mainexc)
+
+    def test_exit(self):
+        self.mainexc = SystemExit(1)
+        pydevd = FakePyDevd('pydevd/pydevd.py', self._main)
+        addr = (None, 8888)
+        with self.assertRaises(SystemExit):
+            run_file(addr, 'spam.py', _pydevd=pydevd, _install=self._install)
+
+        self.assertEqual(self.pydevd, pydevd)
+        self.assertEqual(self.kwargs, {})
+        self.assertEqual(self.maincalls, 1)
+        self.assertEqual(sys.argv, [
+            sys.argv[0],
+            '--port', '8888',
+            '--file', 'spam.py',
+        ])
+        self.assertEqual(ptvsd.wrapper.ptvsd_sys_exit_code, 1)
+
+    def test_installed(self):
+        pydevd = FakePyDevd('pydevd/pydevd.py', self._main)
+        addr = (None, 8888)
+        run_file(addr, 'spam.py', _pydevd=pydevd)
+
+        self.assertIs(pydevd_comm.start_server, ptvsd.wrapper.start_server)
+        self.assertIs(pydevd_comm.start_client, ptvsd.wrapper.start_client)
+        self.assertIs(pydevd.start_server, ptvsd.wrapper.start_server)
+        self.assertIs(pydevd.start_client, ptvsd.wrapper.start_client)
+        __main__ = sys.modules['__main__']
+        self.assertIs(__main__.start_server, ptvsd.wrapper.start_server)
+        self.assertIs(__main__.start_client, ptvsd.wrapper.start_client)
+
+
+class ParseArgsTests(unittest.TestCase):
+
+    def test_module(self):
+        args, extra = parse_args([
+            'eggs',
+            '--port', '8888',
+            '-m', 'spam',
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'module',
+            'name': 'spam',
+            'address': (None, 8888),
+        })
+        self.assertEqual(extra, [])
+
+    def test_script(self):
+        args, extra = parse_args([
+            'eggs',
+            '--port', '8888',
+            'spam.py',
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'script',
+            'name': 'spam.py',
+            'address': (None, 8888),
+        })
+        self.assertEqual(extra, [])
+
+    def test_remote(self):
+        args, extra = parse_args([
+            'eggs',
+            '--host', '1.2.3.4',
+            '--port', '8888',
+            'spam.py',
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'script',
+            'name': 'spam.py',
+            'address': ('1.2.3.4', 8888),
+        })
+        self.assertEqual(extra, [])
+
+    def test_extra(self):
+        args, extra = parse_args([
+            'eggs',
+            '--DEBUG',
+            '--port', '8888',
+            '--vm_type', '???',
+            'spam.py',
+            '--xyz', '123',
+            'abc',
+            '--cmd-line',
+            '--',
+            'foo',
+            '--server',
+            '--bar'
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'script',
+            'name': 'spam.py',
+            'address': (None, 8888),
+        })
+        self.assertEqual(extra, [
+            '--DEBUG',
+            '--vm_type', '???',
+            '--',
+            '--xyz', '123',
+            'abc',
+            '--cmd-line',
+            'foo',
+            '--server',
+            '--bar',
+        ])
+
+    def test_unsupported_arg(self):
+        with self.assertRaises(SystemExit):
+            with captured_stdio():
+                parse_args([
+                    'eggs',
+                    '--port', '8888',
+                    '--xyz', '123',
+                    'spam.py',
+                ])
+
+    def test_backward_compatibility_host(self):
+        args, extra = parse_args([
+            'eggs',
+            '--client', '1.2.3.4',
+            '--port', '8888',
+            '-m', 'spam',
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'module',
+            'name': 'spam',
+            'address': ('1.2.3.4', 8888),
+        })
+        self.assertEqual(extra, [])
+
+    def test_backward_compatibility_module(self):
+        args, extra = parse_args([
+            'eggs',
+            '--port', '8888',
+            '--module',
+            '--file', 'spam:',
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'module',
+            'name': 'spam',
+            'address': (None, 8888),
+        })
+        self.assertEqual(extra, [])
+
+    def test_backward_compatibility_script(self):
+        args, extra = parse_args([
+            'eggs',
+            '--port', '8888',
+            '--file', 'spam.py',
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'script',
+            'name': 'spam.py',
+            'address': (None, 8888),
+        })
+        self.assertEqual(extra, [])
+
+    def test_pseudo_backward_compatibility(self):
+        args, extra = parse_args([
+            'eggs',
+            '--port', '8888',
+            '--module',
+            '--file', 'spam',
+        ])
+
+        self.assertEqual(vars(args), {
+            'kind': 'script',
+            'name': 'spam',
+            'address': (None, 8888),
+        })
+        self.assertEqual(extra, ['--module'])
diff --git a/tests/ptvsd/test_debugger.py b/tests/ptvsd/test_debugger.py
new file mode 100644
--- /dev/null
+++ b/tests/ptvsd/test_debugger.py
@@ -0,0 +1,190 @@
+import sys
+import unittest
+
+from ptvsd.debugger import debug, LOCALHOST
+
+
+class DebugTests(unittest.TestCase):
+
+    def setUp(self):
+        super(DebugTests, self).setUp()
+
+        def _make_run(kind):
+            def run(addr, name, *args, **kwargs):
+                self._run(kind, addr, name, *args, **kwargs)
+            return run
+        self.runners = {}
+        for kind in ('module', 'script', 'code', None):
+            self.runners[kind] = _make_run(kind)
+        self.kind = None
+        self.args = None
+        self.kwargs = None
+
+    def _run(self, kind, *args, **kwargs):
+        self.kind = kind
+        self.args = args
+        self.kwargs = kwargs
+
+    def test_module(self):
+        filename = 'spam'
+        _, port = addr = (LOCALHOST, 8888)
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        debug(filename, port, debug_id, debug_options, 'module',
+              _runners=self.runners, _extra=())
+
+        self.assertEqual(self.kind, 'module')
+        self.assertEqual(self.args, (addr, filename))
+        self.assertEqual(self.kwargs, {})
+
+    def test_script(self):
+        filename = 'spam.py'
+        _, port = addr = (LOCALHOST, 8888)
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        debug(filename, port, debug_id, debug_options, 'script',
+              _runners=self.runners, _extra=())
+
+        self.assertEqual(self.kind, 'script')
+        self.assertEqual(self.args, (addr, filename))
+        self.assertEqual(self.kwargs, {})
+
+    def test_code(self):
+        filename = "print('spam')"
+        _, port = addr = (LOCALHOST, 8888)
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        debug(filename, port, debug_id, debug_options, 'code',
+              _runners=self.runners, _extra=())
+
+        self.assertEqual(self.kind, 'code')
+        self.assertEqual(self.args, (addr, filename))
+        self.assertEqual(self.kwargs, {})
+
+    def test_unsupported(self):
+        filename = 'spam'
+        _, port = addr = (LOCALHOST, 8888)
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        debug(filename, port, debug_id, debug_options, '???',
+              _runners=self.runners, _extra=())
+
+        self.assertIs(self.kind, None)
+        self.assertEqual(self.args, (addr, filename))
+        self.assertEqual(self.kwargs, {})
+
+    def test_extra_sys_argv(self):
+        filename = 'spam.py'
+        _, port = addr = (LOCALHOST, 8888)
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        extra = ['--eggs', 'abc']
+        debug(filename, port, debug_id, debug_options, 'script',
+              _runners=self.runners, _extra=extra)
+
+        self.assertEqual(self.args, (addr, filename, '--eggs', 'abc'))
+
+
+class IntegrationTests(unittest.TestCase):
+
+    def setUp(self):
+        super(IntegrationTests, self).setUp()
+        self.argv = None
+        self.kwargs = None
+        self._sys_argv = list(sys.argv)
+
+    def tearDown(self):
+        sys.argv[:] = self._sys_argv
+        super(IntegrationTests, self).tearDown()
+
+    def _run(self, argv, **kwargs):
+        self.argv = argv
+        self.kwargs = kwargs
+
+    def test_module(self):
+        filename = 'spam'
+        port = 8888
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        sys.argv = [filename]
+        debug(filename, port, debug_id, debug_options, 'module',
+              _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--client', LOCALHOST,
+            '--module',
+            '--file', 'spam:',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_script(self):
+        filename = 'spam.py'
+        port = 8888
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        sys.argv = [filename]
+        debug(filename, port, debug_id, debug_options, 'script',
+              _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--client', LOCALHOST,
+            '--file', 'spam.py',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_code(self):
+        filename = "print('spam')"
+        port = 8888
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        sys.argv = [filename]
+        debug(filename, port, debug_id, debug_options, 'code',
+              _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--client', LOCALHOST,
+            '--file', filename,
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    def test_unsupported(self):
+        filename = 'spam'
+        port = 8888
+        debug_id = 1
+        debug_options = {'x': 'y'}
+        sys.argv = [filename]
+        debug(filename, port, debug_id, debug_options, '???',
+              _run=self._run, _prog='eggs')
+
+        self.assertEqual(self.argv, [
+            'eggs',
+            '--port', '8888',
+            '--client', LOCALHOST,
+            '--file', 'spam',
+        ])
+        self.assertEqual(self.kwargs, {})
+
+    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, [
+            'eggs',
+            '--port', '8888',
+            '--client', LOCALHOST,
+            '--file', 'spam.py',
+            '--abc', 'xyz',
+            '42',
+        ])
+        self.assertEqual(self.kwargs, {})