Repository: microsoft/ptvsd

Issue:
Args passed to user script on 'start without debugging' contains ptvsd args
## Environment data

- PTVSD version: Master
- OS and version: Windows 10
- Python version (& distribution if applicable, e.g. Anaconda): Any
- Using VS Code or Visual Studio: VSC

## Actual behavior

```
['c:\\Users\\kanadig\\.vscode\\extensions\\ms-python.python-2018.6.0\\pythonFiles\\experimental\\ptvsd\\ptvsd\\__main__.py', '--nodebug', '--host', 'localhost', '--port', '51225', 'c:\\scratch\\test.py', '--one', '--two', '--three']
```

## Expected  behavior

```
['c:\\scratch\\test.py', '--one', '--two', '--three']
```

## Steps to reproduce:
1. Create a script file with this content:
```python
import sys
print(sys.argv)
```
2. Add `args` to python experimental launch configuration:
```json
{
    "name": "PyExp: Current File",
    "type": "pythonExperimental",
    "request": "launch",
    "program": "${file}",
    "console": "integratedTerminal",
    "args": ["--one", "--two", "--three"]
}
```
2. Run using **F5** and **Ctrl+F5**, the output should be same.




Discussion:
@karthiknadig  I'll be creating tests for this, or you could do it instead, either or.

I will do it when i fix the issue.

Resolving patch:
diff --git a/ptvsd/__main__.py b/ptvsd/__main__.py
--- a/ptvsd/__main__.py
+++ b/ptvsd/__main__.py
@@ -49,19 +49,6 @@
 """  # noqa
 
 
-PYDEVD_DEFAULTS = {
-    '--qt-support=auto',
-}
-
-
-def _set_pydevd_defaults(pydevd_args):
-    args_to_append = []
-    for arg in PYDEVD_DEFAULTS:
-        if arg not in pydevd_args:
-            args_to_append.append(arg)
-    return pydevd_args + args_to_append
-
-
 def parse_args(argv=None):
     """Return the parsed args to use in main()."""
     if argv is None:
@@ -75,7 +62,7 @@ def parse_args(argv=None):
 
     supported, pydevd, script = _group_args(argv)
     args = _parse_args(prog, supported)
-    pydevd = _set_pydevd_defaults(pydevd)
+    # '--' is used in _run_args to extract pydevd specific args
     extra = pydevd + ['--']
     if script:
         extra += script
diff --git a/ptvsd/_local.py b/ptvsd/_local.py
--- a/ptvsd/_local.py
+++ b/ptvsd/_local.py
@@ -7,6 +7,19 @@
 from ptvsd.socket import Address
 
 
+PYDEVD_DEFAULTS = {
+    '--qt-support=auto',
+}
+
+
+def _set_pydevd_defaults(pydevd_args):
+    args_to_append = []
+    for arg in PYDEVD_DEFAULTS:
+        if arg not in pydevd_args:
+            args_to_append.append(arg)
+    return pydevd_args + args_to_append
+
+
 ########################
 # high-level functions
 
@@ -18,7 +31,10 @@ def debug_main(address, name, kind, *extra, **kwargs):
 
 
 def run_main(address, name, kind, *extra, **kwargs):
-    no_debug_runner(address, name, kind == 'module', *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)
 
 
 ########################
@@ -57,6 +73,7 @@ def _run_argv(address, filename, extra, _prog=sys.argv[0]):
         pydevd = []
         extra = list(extra)
 
+    pydevd = _set_pydevd_defaults(pydevd)
     host, port = address
     argv = [
         _prog,
@@ -71,6 +88,15 @@ def _run_argv(address, filename, extra, _prog=sys.argv[0]):
     ] + extra
 
 
+def _run_main_argv(filename, extra):
+    if '--' in extra:
+        pydevd = list(extra[:extra.index('--')])
+        extra = list(extra[len(pydevd) + 1:])
+    else:
+        extra = list(extra)
+    return [filename] + extra
+
+
 def _run(argv, addr, _pydevd=pydevd, _install=install, **kwargs):
     """Start pydevd with the given commandline args."""
     #print(' '.join(argv))


Test patch:
diff --git a/tests/helpers/argshelper.py b/tests/helpers/argshelper.py
new file mode 100644
--- /dev/null
+++ b/tests/helpers/argshelper.py
@@ -0,0 +1,11 @@
+PROG = 'eggs'
+PORT_ARGS = ['--port', '8888']
+PYDEVD_DEFAULT_ARGS = ['--qt-support=auto']
+
+
+def _get_args(*args, **kwargs):
+    ptvsd_extras = kwargs.get('ptvsd_extras', [])
+    prog = [kwargs.get('prog', PROG)]
+    port = kwargs.get('port', PORT_ARGS)
+    pydevd_args = kwargs.get('pydevd', PYDEVD_DEFAULT_ARGS)
+    return prog + port + ptvsd_extras + pydevd_args + list(args)
diff --git a/tests/ptvsd/test___main__.py b/tests/ptvsd/test___main__.py
--- a/tests/ptvsd/test___main__.py
+++ b/tests/ptvsd/test___main__.py
@@ -7,7 +7,7 @@
 
 class ParseArgsTests(unittest.TestCase):
 
-    EXPECTED_EXTRA = ['--qt-support=auto', '--']
+    EXPECTED_EXTRA = ['--']
 
     def test_module(self):
         args, extra = parse_args([
@@ -222,7 +222,7 @@ def test_extra(self):
         self.assertEqual(extra, [
             '--DEBUG',
             '--vm_type', '???',
-            '--qt-support=auto', '--',  # Expected pydevd defaults
+            '--',  # Expected pydevd defaults separator
             '--xyz', '123',
             'abc',
             '--cmd-line',
@@ -258,7 +258,7 @@ def test_extra_nodebug(self):
         self.assertEqual(extra, [
             '--DEBUG',
             '--vm_type', '???',
-            '--qt-support=auto', '--',  # Expected pydevd defaults
+            '--',  # Expected pydevd defaults separator
             '--xyz', '123',
             'abc',
             '--cmd-line',
diff --git a/tests/ptvsd/test_debugger.py b/tests/ptvsd/test_debugger.py
--- a/tests/ptvsd/test_debugger.py
+++ b/tests/ptvsd/test_debugger.py
@@ -3,6 +3,7 @@
 
 from ptvsd.debugger import debug, LOCALHOST
 from ptvsd.socket import Address
+from tests.helpers.argshelper import _get_args
 
 
 class DebugTests(unittest.TestCase):
@@ -124,13 +125,10 @@ def test_module(self):
         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.argv,
+                         _get_args('--module', '--file', 'spam:',
+                                   ptvsd_extras=['--client', LOCALHOST]))
+
         self.assertEqual(self.addr, Address.as_client(None, port))
         self.assertEqual(self.kwargs, {
             'singlesession': True,
@@ -145,12 +143,9 @@ def test_script(self):
         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.argv,
+                         _get_args('--file', 'spam.py',
+                                   ptvsd_extras=['--client', LOCALHOST]))
         self.assertEqual(self.addr, Address.as_client(None, port))
         self.assertEqual(self.kwargs, {
             'singlesession': True,
@@ -165,12 +160,9 @@ def test_code(self):
         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.argv,
+                         _get_args('--file', filename,
+                                   ptvsd_extras=['--client', LOCALHOST]))
         self.assertEqual(self.addr, Address.as_client(None, port))
         self.assertEqual(self.kwargs, {
             'singlesession': True,
@@ -185,12 +177,9 @@ def test_unsupported(self):
         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.argv,
+                         _get_args('--file', 'spam',
+                                   ptvsd_extras=['--client', LOCALHOST]))
         self.assertEqual(self.addr, Address.as_client(None, port))
         self.assertEqual(self.kwargs, {
             'singlesession': True,
@@ -205,14 +194,9 @@ def test_extra_sys_argv(self):
         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.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,
diff --git a/tests/ptvsd/test_local.py b/tests/ptvsd/test_local.py
--- a/tests/ptvsd/test_local.py
+++ b/tests/ptvsd/test_local.py
@@ -5,7 +5,8 @@
 
 import ptvsd.pydevd_hooks
 from ptvsd.socket import Address
-from ptvsd._local import run_module, run_file
+from ptvsd._local import run_module, run_file, run_main
+from tests.helpers.argshelper import _get_args
 
 
 class FakePyDevd(object):
@@ -35,6 +36,14 @@ def _run(self, argv, addr, **kwargs):
         self.addr = addr
         self.kwargs = kwargs
 
+    def _no_debug_runner(self, addr, filename, is_module, *extras, **kwargs):
+        self.addr = addr
+        self.argv = sys.argv
+        self.filename = filename
+        self.is_module = is_module
+        self.args = extras
+        self.kwargs = kwargs
+
 
 class RunModuleTests(RunBase, unittest.TestCase):
 
@@ -42,12 +51,9 @@ 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.argv,
+                         _get_args('--module', '--file', 'spam:'))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -55,12 +61,9 @@ 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, [
-            'eggs',
-            '--port', '8888',
-            '--module',
-            '--file', 'spam:',
-        ])
+        self.assertEqual(self.argv,
+                         _get_args('--module', '--file', 'spam:'))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -68,13 +71,10 @@ 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.argv,
+                         _get_args('--module', '--file', 'spam:',
+                                   ptvsd_extras=['--client', '1.2.3.4']))
+
         self.assertEqual(self.addr, Address.as_client(*addr))
         self.assertEqual(self.kwargs, {
             'singlesession': True,
@@ -84,13 +84,11 @@ def test_remote_localhost(self):
         addr = Address.as_client(None, 8888)
         run_module(addr, 'spam', _run=self._run, _prog='eggs')
 
-        self.assertEqual(self.argv, [
-            'eggs',
-            '--port', '8888',
-            '--client', 'localhost',
-            '--module',
-            '--file', 'spam:',
-        ])
+        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,
@@ -101,14 +99,11 @@ def test_extra(self):
         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.argv,
+                         _get_args(
+                             '--module', '--file', 'spam:', '--DEBUG',
+                             ptvsd_extras=['--vm_type', 'xyz']))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -116,12 +111,11 @@ 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.argv,
+                         _get_args(
+                             '--module', '--file', 'spam:',
+                             prog=sys.argv[0]))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -132,11 +126,9 @@ 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.argv,
+                         _get_args('--file', 'spam.py'))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -144,11 +136,9 @@ def test_server(self):
         addr = Address.as_server('10.0.1.1', 8888)
         run_file(addr, 'spam.py', _run=self._run, _prog='eggs')
 
-        self.assertEqual(self.argv, [
-            'eggs',
-            '--port', '8888',
-            '--file', 'spam.py',
-        ])
+        self.assertEqual(self.argv,
+                         _get_args('--file', 'spam.py'))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -156,12 +146,11 @@ 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.argv,
+                         _get_args(
+                             '--file', 'spam.py',
+                             ptvsd_extras=['--client', '1.2.3.4']))
+
         self.assertEqual(self.addr, Address.as_client(*addr))
         self.assertEqual(self.kwargs, {
             'singlesession': True,
@@ -171,12 +160,11 @@ def test_remote_localhost(self):
         addr = Address.as_client(None, 8888)
         run_file(addr, 'spam.py', _run=self._run, _prog='eggs')
 
-        self.assertEqual(self.argv, [
-            'eggs',
-            '--port', '8888',
-            '--client', 'localhost',
-            '--file', 'spam.py',
-        ])
+        self.assertEqual(self.argv,
+                         _get_args(
+                             '--file', 'spam.py',
+                             ptvsd_extras=['--client', 'localhost']))
+
         self.assertEqual(self.addr, Address.as_client(*addr))
         self.assertEqual(self.kwargs, {
             'singlesession': True,
@@ -187,13 +175,11 @@ def test_extra(self):
         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.argv,
+                         _get_args(
+                             '--file', 'spam.py', '--DEBUG',
+                             ptvsd_extras=['--vm_type', 'xyz']))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -201,11 +187,9 @@ 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.argv,
+                         _get_args('--file', 'spam.py', prog=sys.argv[0]))
+
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
 
@@ -255,11 +239,8 @@ def test_run(self):
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
         self.assertEqual(self.maincalls, 1)
-        self.assertEqual(sys.argv, [
-            sys.argv[0],
-            '--port', '8888',
-            '--file', 'spam.py',
-        ])
+        self.assertEqual(sys.argv,
+                         _get_args('--file', 'spam.py', prog=sys.argv[0]))
         self.assertEqual(self.exitcode, -1)
 
     def test_failure(self):
@@ -274,11 +255,8 @@ def test_failure(self):
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
         self.assertEqual(self.maincalls, 1)
-        self.assertEqual(sys.argv, [
-            sys.argv[0],
-            '--port', '8888',
-            '--file', 'spam.py',
-        ])
+        self.assertEqual(sys.argv,
+                         _get_args('--file', 'spam.py', prog=sys.argv[0]))
         self.assertEqual(self.exitcode, -1)  # TODO: Is this right?
         self.assertIs(exc, self.mainexc)
 
@@ -293,11 +271,8 @@ def test_exit(self):
         self.assertEqual(self.addr, Address.as_server(*addr))
         self.assertEqual(self.kwargs, {})
         self.assertEqual(self.maincalls, 1)
-        self.assertEqual(sys.argv, [
-            sys.argv[0],
-            '--port', '8888',
-            '--file', 'spam.py',
-        ])
+        self.assertEqual(sys.argv,
+                         _get_args('--file', 'spam.py', prog=sys.argv[0]))
         self.assertEqual(self.exitcode, 1)
 
     def test_installed(self):
@@ -320,3 +295,55 @@ def test_installed(self):
                           '(module {})'.format(mod.__name__))
             self.assertIs(start_client, expected_client,
                           '(module {})'.format(mod.__name__))
+
+
+class NoDebugRunTests(RunBase, unittest.TestCase):
+    def test_nodebug_script_args(self):
+        addr = (None, 8888)
+        args = ('--one', '--two', '--three')
+        run_main(addr, 'spam.py', 'script', *args,
+                 _runner=self._no_debug_runner)
+
+        self.assertEqual(self.argv, ['spam.py'] + list(args))
+
+        self.assertEqual(self.addr, Address.as_server(*addr))
+        self.assertFalse(self.is_module)
+        self.assertEqual(self.args, args)
+        self.assertEqual(self.kwargs, {})
+
+    def test_nodebug_script_no_args(self):
+        addr = Address.as_server('10.0.1.1', 8888)
+        run_main(addr, 'spam.py', 'script',
+                 _runner=self._no_debug_runner)
+
+        self.assertEqual(self.argv, ['spam.py'])
+
+        self.assertEqual(self.addr, Address.as_server(*addr))
+        self.assertFalse(self.is_module)
+        self.assertEqual(self.args, ())
+        self.assertEqual(self.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_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, {})