Merge pull request #939 from int19h/896-master

Merge changes from v4.1.4-branch into master.
This commit is contained in:
Pavel Minaev 2018-10-19 15:00:37 -07:00 committed by GitHub
commit 26cfd18f99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 87 additions and 208 deletions

View file

@ -6,70 +6,69 @@
[![PyPI](https://img.shields.io/pypi/v/ptvsd.svg)](https://pypi.org/project/ptvsd/)
## `ptvsd` CLI Usage
### Debug a script file
Use this to launch your script file. Launch script file without waiting for debugger to attach.
### Debugging a script file
To run a script file with debugging enabled, but without waiting for the debugger to attach (i.e. code starts executing immediately):
```console
-m ptvsd --port 5678 myfile.py
-m ptvsd --host localhost --port 5678 myfile.py
```
If you want the debugger to attach before running your code use `--wait` flag.
To wait until the debugger attaches before running your code, use the `--wait` switch.
```console
-m ptvsd --port 5678 --wait myfile.py
-m ptvsd --host localhost --port 5678 --wait myfile.py
```
The `--host` option specifies the interface on which the debug server is listening for connections. To be able to attach from another machine, make sure that the server is listening on a public interface - using `0.0.0.0` will make it listen on all available interfaces:
```console
-m ptvsd --host 0.0.0.0 --port 5678 myfile.py
```
This should only be done on secure networks, since anyone who can connect to the specified port can then execute arbitrary code within the debugged process.
### Debug a module
Use this to launch your module. Launch script file without waiting for debugger to attach.
```console
-m ptvsd --port 5678 -m mymodule
```
If you want the debugger to attach before running your code use `--wait` flag.
```console
-m ptvsd --port 5678 --wait -m mymodule
```
To pass arguments to the script, just specify them after the filename. This works the same as with Python itself - everything up to the filename is processed by ptvsd, but everything after that becomes `sys.argv` of the running process.
### Debug a process by id
Attach to a process running python code.
### Debugging a module
To run a module, use the `-m` switch instead of filename:
```console
-m ptvsd --host 0.0.0.0 --port 5678 --pid 12345
-m ptvsd --host localhost --port 5678 -m mymodule
```
Same as with scripts, command line arguments can be passed to the module by specifying them after the module name. All other ptvsd switches work identically in this mode; in particular, `--wait` can be used to block execution until debugger attaches.
### Attaching to a running process by ID
The following command injects the debugger into a process with a given PID that is running Python code. Once the command returns, a ptvsd server is running within the process, as if that process was launched via `-m ptvsd` itself.
```console
-m ptvsd --host localhost --port 5678 --pid 12345
```
## `ptvsd` Import usage
### Enable debugging
In your script import ptvsd and call `enable_attach` to enable the process to attach to the debugger. The default port is 5678. You can configure this while calling `enable_attach`.
### Enabling debugging
At the beginning of your script, import ptvsd, and call `ptvsd.enable_attach()` to start the debug server. The default hostname is `0.0.0.0`, and the default port is 5678; these can be overridden by passing a `(host, port)` tuple as the first argument of `enable_attach()`.
```python
import ptvsd
ptvsd.enable_attach()
# your code
...
```
### Wait for attach
Use the `wait_for_attach()` function to block execution until debugger is attached.
### Waiting for debugger to attach
Use the `ptvsd.wait_for_attach()` function to block program execution until debugger is attached.
```python
import ptvsd
ptvsd.enable_attach()
# script execution will stop here till debugger is attached
ptvsd.wait_for_attach()
# your code
ptvsd.wait_for_attach() # blocks execution until debugger is attached
...
```
### `breakpoint()` function
In python >= 3.7, `ptvsd` supports the `breakpoint()` function. Use `break_into_debugger()` function for similar behavior and compatibility with older versions of python (2.7 and >= 3.4). These functions will block only if the debugger is attached.
In Python 3.7 and above, `ptvsd` supports the standard `breakpoint()` function. Use `ptvsd.break_into_debugger()` function for similar behavior and compatibility with older versions of Python (3.6 and below). If the debugger is attached when either of these functions are invoked, it will pause execution on the calling line, as if it had a breakpoint set. If there's no debugger attached, the functions do nothing, and code continues to execute normally.
```python
import ptvsd
ptvsd.enable_attach()
while True:
# your code
breakpoint() # ptvsd.break_into_debugger()
# your code
...
breakpoint() # or ptvsd.break_into_debugger() on <3.7
...
```
## Custom Protocol arguments
### Launch request arguments
```js
```json5
{
"debugOptions": [
"RedirectOutput", // Whether to redirect stdout and stderr (see pydevd_comm.CMD_REDIRECT_OUTPUT)
@ -86,7 +85,7 @@ while True:
```
### Attach request arguments
```js
```json5
{
"debugOptions": [
"RedirectOutput", // Whether to redirect stdout and stderr (see pydevd_comm.CMD_REDIRECT_OUTPUT)

View file

@ -25,8 +25,6 @@ For the PyDevd CLI handling see:
PYDEVD_OPTS = {
'--file',
'--client',
#'--port',
'--vm_type',
}
@ -99,9 +97,7 @@ def _group_args(argv):
if gottarget:
script = argv[i:] + script
break
if arg == '--client':
arg = '--host'
elif arg == '--file':
if arg == '--file':
if nextarg is None: # The filename is missing...
pydevd.append(arg)
continue # This will get handled later.
@ -124,14 +120,14 @@ def _group_args(argv):
supported.append(arg)
# ptvsd support
elif arg in ('--host', '--server-host', '--port', '--pid', '-m', '-c', '--subprocess-of', '--subprocess-notify'):
elif arg in ('--host', '--port', '--pid', '-m', '-c', '--subprocess-of', '--subprocess-notify'):
if arg in ('-m', '-c', '--pid'):
gottarget = True
supported.append(arg)
if nextarg is not None:
supported.append(nextarg)
skip += 1
elif arg in ('--single-session', '--wait'):
elif arg in ('--single-session', '--wait', '--client'):
supported.append(arg)
elif arg == '--multiprocess':
supported.append(arg)
@ -152,10 +148,9 @@ def _parse_args(prog, argv):
parser = argparse.ArgumentParser(prog=prog)
parser.add_argument('--nodebug', action='store_true')
parser.add_argument('--client', action='store_true')
host = parser.add_mutually_exclusive_group()
host.add_argument('--host')
host.add_argument('--server-host')
parser.add_argument('--host', required=True)
parser.add_argument('--port', type=int, required=True)
def port_range(arg):
@ -183,17 +178,10 @@ def _parse_args(prog, argv):
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'))
host = ns.pop('host', None)
port = ns.pop('port')
client = ns.pop('client')
args.address = (Address.as_client if client else Address.as_server)(host, port) # noqa
if ns['multiprocess']:
options.multiprocess = True

View file

@ -242,7 +242,7 @@ def patch_args(args):
# itself can remain as is, because ptvsd is compatible with Python in that respect.
args[i:i] = [
'-m', 'ptvsd',
'--server-host', 'localhost',
'--host', 'localhost',
'--port', '0',
'--wait',
'--multiprocess',

View file

@ -133,10 +133,11 @@ class DebugSession(object):
argv += ['-m', 'ptvsd']
if self.method == 'attach_socket':
argv += ['--port', str(self.ptvsd_port), '--wait']
argv += ['--wait']
else:
self._listen()
argv += ['--host', 'localhost', '--port', str(self.ptvsd_port)]
argv += ['--client']
argv += ['--host', 'localhost', '--port', str(self.ptvsd_port)]
if self.multiprocess:
argv += ['--multiprocess']

View file

@ -235,20 +235,19 @@ class DebugAdapter(Closeable):
def _ensure_addr(cls, argv, addr):
if '--host' in argv:
raise ValueError("unexpected '--host' in argv")
if '--server-host' in argv:
raise ValueError("unexpected '--server-host' in argv")
if '--port' in argv:
raise ValueError("unexpected '--port' in argv")
if '--client' in argv:
raise ValueError("unexpected '--client' in argv")
host, port = addr
argv.insert(0, str(port))
argv.insert(0, '--port')
argv.insert(0, host)
if addr.isserver:
argv.insert(0, '--server-host')
else:
argv.insert(0, '--host')
argv.insert(0, '--host')
if not addr.isserver:
argv.insert(0, '--client')
def __init__(self, proc, addr, owned=False):
super(DebugAdapter, self).__init__()

View file

@ -9,28 +9,18 @@ class ParseArgsTests(unittest.TestCase):
EXPECTED_EXTRA = ['--']
def test_module(self):
args, extra = parse_args([
'eggs',
'--port', '8888',
'-m', 'spam',
])
self.assertEqual(vars(args), {
'kind': 'module',
'name': 'spam',
'address': Address.as_server(None, 8888),
'nodebug': False,
'single_session': False,
'wait': False,
'multiprocess': False,
})
self.assertEqual(extra, self.EXPECTED_EXTRA)
def test_host_required(self):
with self.assertRaises(SystemExit):
parse_args([
'eggs',
'--port', '8888',
'-m', 'spam',
])
def test_module_server(self):
args, extra = parse_args([
'eggs',
'--server-host', '10.0.1.1',
'--host', '10.0.1.1',
'--port', '8888',
'-m', 'spam',
])
@ -50,6 +40,8 @@ class ParseArgsTests(unittest.TestCase):
args, extra = parse_args([
'eggs',
'--nodebug',
'--client',
'--host', 'localhost',
'--port', '8888',
'-m', 'spam',
])
@ -57,7 +49,7 @@ class ParseArgsTests(unittest.TestCase):
self.assertEqual(vars(args), {
'kind': 'module',
'name': 'spam',
'address': Address.as_client(None, 8888),
'address': Address.as_client('localhost', 8888),
'nodebug': True,
'single_session': False,
'wait': False,
@ -68,6 +60,7 @@ class ParseArgsTests(unittest.TestCase):
def test_script(self):
args, extra = parse_args([
'eggs',
'--host', 'localhost',
'--port', '8888',
'spam.py',
])
@ -75,7 +68,7 @@ class ParseArgsTests(unittest.TestCase):
self.assertEqual(vars(args), {
'kind': 'script',
'name': 'spam.py',
'address': Address.as_server(None, 8888),
'address': Address.as_server('localhost', 8888),
'nodebug': False,
'single_session': False,
'wait': False,
@ -86,7 +79,7 @@ class ParseArgsTests(unittest.TestCase):
def test_script_server(self):
args, extra = parse_args([
'eggs',
'--server-host', '10.0.1.1',
'--host', '10.0.1.1',
'--port', '8888',
'spam.py',
])
@ -106,6 +99,8 @@ class ParseArgsTests(unittest.TestCase):
args, extra = parse_args([
'eggs',
'--nodebug',
'--client',
'--host', 'localhost',
'--port', '8888',
'spam.py',
])
@ -113,7 +108,7 @@ class ParseArgsTests(unittest.TestCase):
self.assertEqual(vars(args), {
'kind': 'script',
'name': 'spam.py',
'address': Address.as_client(None, 8888),
'address': Address.as_client('localhost', 8888),
'nodebug': True,
'single_session': False,
'wait': False,
@ -124,6 +119,7 @@ class ParseArgsTests(unittest.TestCase):
def test_remote(self):
args, extra = parse_args([
'eggs',
'--client',
'--host', '1.2.3.4',
'--port', '8888',
'spam.py',
@ -143,6 +139,7 @@ class ParseArgsTests(unittest.TestCase):
def test_remote_localhost(self):
args, extra = parse_args([
'eggs',
'--client',
'--host', 'localhost',
'--port', '8888',
'spam.py',
@ -163,6 +160,7 @@ class ParseArgsTests(unittest.TestCase):
args, extra = parse_args([
'eggs',
'--nodebug',
'--client',
'--host', '1.2.3.4',
'--port', '8888',
'spam.py',
@ -183,6 +181,7 @@ class ParseArgsTests(unittest.TestCase):
args, extra = parse_args([
'eggs',
'--single-session',
'--host', 'localhost',
'--port', '8888',
'spam.py',
])
@ -202,7 +201,7 @@ class ParseArgsTests(unittest.TestCase):
args, extra = parse_args([
'eggs',
'--single-session',
'--server-host', '1.2.3.4',
'--host', '1.2.3.4',
'--port', '8888',
'spam.py',
])
@ -221,6 +220,7 @@ class ParseArgsTests(unittest.TestCase):
def test_remote_wait(self):
args, extra = parse_args([
'eggs',
'--client',
'--host', '1.2.3.4',
'--port', '8888',
'--wait',
@ -242,6 +242,7 @@ class ParseArgsTests(unittest.TestCase):
args, extra = parse_args([
'eggs',
'--DEBUG',
'--host', 'localhost',
'--port', '8888',
'--vm_type', '???',
'spam.py',
@ -257,7 +258,7 @@ class ParseArgsTests(unittest.TestCase):
self.assertEqual(vars(args), {
'kind': 'script',
'name': 'spam.py',
'address': Address.as_server(None, 8888),
'address': Address.as_server('localhost', 8888),
'nodebug': False,
'single_session': False,
'wait': False,
@ -280,6 +281,8 @@ class ParseArgsTests(unittest.TestCase):
'eggs',
'--DEBUG',
'--nodebug',
'--client',
'--host', 'localhost',
'--port', '8888',
'--vm_type', '???',
'spam.py',
@ -295,7 +298,7 @@ class ParseArgsTests(unittest.TestCase):
self.assertEqual(vars(args), {
'kind': 'script',
'name': 'spam.py',
'address': Address.as_client(None, 8888),
'address': Address.as_client('localhost', 8888),
'nodebug': True,
'single_session': False,
'wait': False,
@ -342,124 +345,10 @@ class ParseArgsTests(unittest.TestCase):
'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': Address.as_client('1.2.3.4', 8888),
'nodebug': False,
'single_session': False,
'wait': False,
'multiprocess': False,
})
self.assertEqual(extra, self.EXPECTED_EXTRA)
def test_backward_compatibility_host_nodebug(self):
args, extra = parse_args([
'eggs',
'--nodebug',
'--client', '1.2.3.4',
'--port', '8888',
'-m', 'spam',
])
self.assertEqual(vars(args), {
'kind': 'module',
'name': 'spam',
'address': Address.as_client('1.2.3.4', 8888),
'nodebug': True,
'single_session': False,
'wait': False,
'multiprocess': False,
})
self.assertEqual(extra, self.EXPECTED_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': Address.as_server(None, 8888),
'nodebug': False,
'single_session': False,
'wait': False,
'multiprocess': False,
})
self.assertEqual(extra, self.EXPECTED_EXTRA)
def test_backward_compatibility_module_nodebug(self):
args, extra = parse_args([
'eggs',
'--nodebug',
'--port', '8888',
'--module',
'--file', 'spam:',
])
self.assertEqual(vars(args), {
'kind': 'module',
'name': 'spam',
'address': Address.as_client(None, 8888),
'nodebug': True,
'single_session': False,
'wait': False,
'multiprocess': False,
})
self.assertEqual(extra, self.EXPECTED_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': Address.as_server(None, 8888),
'nodebug': False,
'single_session': False,
'wait': False,
'multiprocess': False,
})
self.assertEqual(extra, self.EXPECTED_EXTRA)
def test_backward_compatibility_script_nodebug(self):
args, extra = parse_args([
'eggs',
'--nodebug',
'--port', '8888',
'--file', 'spam.py',
])
self.assertEqual(vars(args), {
'kind': 'script',
'name': 'spam.py',
'address': Address.as_client(None, 8888),
'nodebug': True,
'single_session': False,
'wait': False,
'multiprocess': False,
})
self.assertEqual(extra, self.EXPECTED_EXTRA)
def test_pseudo_backward_compatibility(self):
args, extra = parse_args([
'eggs',
'--host', 'localhost',
'--port', '8888',
'--module',
'--file', 'spam',
@ -468,7 +357,7 @@ class ParseArgsTests(unittest.TestCase):
self.assertEqual(vars(args), {
'kind': 'script',
'name': 'spam',
'address': Address.as_server(None, 8888),
'address': Address.as_server('localhost', 8888),
'nodebug': False,
'single_session': False,
'wait': False,
@ -480,6 +369,8 @@ class ParseArgsTests(unittest.TestCase):
args, extra = parse_args([
'eggs',
'--nodebug',
'--client',
'--host', 'localhost',
'--port', '8888',
'--module',
'--file', 'spam',
@ -488,7 +379,7 @@ class ParseArgsTests(unittest.TestCase):
self.assertEqual(vars(args), {
'kind': 'script',
'name': 'spam',
'address': Address.as_client(None, 8888),
'address': Address.as_client('localhost', 8888),
'nodebug': True,
'single_session': False,
'wait': False,

View file

@ -115,6 +115,7 @@ class RawConnectionTests(unittest.TestCase):
proc = Proc.start_python_module('ptvsd', [
'--server',
'--wait',
'--host', 'localhost',
'--port', '5678',
'--file', filename,
], env={