diff --git a/.flake8 b/.flake8 index 62aba857..b1da661b 100644 --- a/.flake8 +++ b/.flake8 @@ -1,7 +1,7 @@ [flake8] ignore = W, E24,E121,E123,E125,E126,E221,E226,E266,E704, - E265,E722,E501,E731,E306,E401,E302,E222 + E265,E722,E501,E731,E306,E401,E302,E222,E303 exclude = ptvsd/_vendored/pydevd, ./.eggs, diff --git a/ptvsd/__main__.py b/ptvsd/__main__.py index 2053c5ef..17b21121 100644 --- a/ptvsd/__main__.py +++ b/ptvsd/__main__.py @@ -6,7 +6,7 @@ import argparse import os.path import sys -from ptvsd import multiproc +from ptvsd import multiproc, options from ptvsd._attach import attach_main from ptvsd._local import debug_main, run_main from ptvsd.socket import Address @@ -124,8 +124,8 @@ def _group_args(argv): supported.append(arg) # ptvsd support - elif arg in ('--host', '--server-host', '--port', '--pid', '-m', '--multiprocess-port-range'): - if arg in ('-m', '--pid'): + elif arg in ('--host', '--server-host', '--port', '--pid', '-m', '-c', '--multiprocess-port-range'): + if arg in ('-m', '-c', '--pid'): gottarget = True supported.append(arg) if nextarg is not None: @@ -169,6 +169,7 @@ def _parse_args(prog, argv): target = parser.add_mutually_exclusive_group(required=True) target.add_argument('-m', dest='module') + target.add_argument('-c', dest='code') target.add_argument('--pid', type=int) target.add_argument('filename', nargs='?') @@ -205,12 +206,17 @@ def _parse_args(prog, argv): pid = ns.pop('pid') module = ns.pop('module') filename = ns.pop('filename') + code = ns.pop('code') if pid is not None: args.name = pid args.kind = 'pid' elif module is not None: args.name = module args.kind = 'module' + elif code is not None: + options.code = code + args.name = 'ptvsd.run_code' + args.kind = 'module' else: args.name = filename args.kind = 'script' diff --git a/ptvsd/multiproc.py b/ptvsd/multiproc.py index 6f1a2a30..a610fe7d 100644 --- a/ptvsd/multiproc.py +++ b/ptvsd/multiproc.py @@ -55,6 +55,7 @@ def disable(): except Exception: pass + def _listener(): counter = itertools.count(1) while listener_port: diff --git a/ptvsd/options.py b/ptvsd/options.py new file mode 100644 index 00000000..c554bd8b --- /dev/null +++ b/ptvsd/options.py @@ -0,0 +1,13 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE in the project root +# for license information. + +from __future__ import print_function, with_statement, absolute_import + + +"""ptvsd command-line options that need to be globally available. +""" + +code = None +"""When running with -c, specifies the code that needs to be run. +""" diff --git a/ptvsd/run_code.py b/ptvsd/run_code.py new file mode 100644 index 00000000..a0adb982 --- /dev/null +++ b/ptvsd/run_code.py @@ -0,0 +1,15 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE in the project root +# for license information. + +# This module is used to implement -c support, since pydevd doesn't support +# it directly. So we tell it to run this module instead, and it just does +# exec on the code. + +# It is crucial that this module does *not* do "from __future__ import ...", +# because we want exec below to use the defaults as defined by the flags that +# were passed to the Python interpreter when it was launched. + +if __name__ == '__main__': + from ptvsd.options import code + exec(code, {}) diff --git a/pytests/conftest.py b/pytests/conftest.py index 3382ecb3..b520ee84 100644 --- a/pytests/conftest.py +++ b/pytests/conftest.py @@ -126,8 +126,8 @@ def pyfile(request, tmpdir): ]) def debug_session(request): session = DebugSession(request.param) - yield session try: + yield session try: failed = request.node.call_result.failed except AttributeError: diff --git a/pytests/func/test_run.py b/pytests/func/test_run.py index cdc1ec39..dc8f483c 100644 --- a/pytests/func/test_run.py +++ b/pytests/func/test_run.py @@ -5,13 +5,17 @@ from __future__ import print_function, with_statement, absolute_import import os +import pytest + import ptvsd -from ..helpers.pattern import ANY -from ..helpers.timeline import Event +from pytests.helpers import print +from pytests.helpers.pattern import ANY +from pytests.helpers.timeline import Event -def test_run(debug_session, pyfile): +@pytest.mark.parametrize('run_as', ['file', 'module', 'code']) +def test_run(debug_session, pyfile, run_as): @pyfile def code_to_debug(): import os @@ -23,13 +27,24 @@ def test_run(debug_session, pyfile): backchannel.write_json(os.path.abspath(sys.modules['ptvsd'].__file__)) print('end') - debug_session.prepare_to_run(filename=code_to_debug, backchannel=True) + if run_as == 'file': + debug_session.prepare_to_run(filename=code_to_debug, backchannel=True) + elif run_as == 'module': + debug_session.add_file_to_pythonpath(code_to_debug) + debug_session.prepare_to_run(module='code_to_debug', backchannel=True) + elif run_as == 'code': + with open(code_to_debug, 'r') as f: + code = f.read() + debug_session.prepare_to_run(code=code, backchannel=True) + else: + pytest.fail() + debug_session.start_debugging() assert debug_session.timeline.is_frozen process_event, = debug_session.all_occurrences_of(Event('process')) assert process_event == Event('process', ANY.dict_with({ - 'name': code_to_debug, + 'name': ANY if run_as == 'code' else code_to_debug, })) debug_session.write_json('continue') diff --git a/pytests/helpers/colors.py b/pytests/helpers/colors.py index 220e1a3c..bac77319 100644 --- a/pytests/helpers/colors.py +++ b/pytests/helpers/colors.py @@ -4,54 +4,89 @@ from __future__ import print_function, with_statement, absolute_import -from colorama import Fore -from pygments import highlight, lexers, formatters, token +import platform -# Colors that are commented out don't work with PowerShell. -RESET = Fore.RESET -BLACK = Fore.BLACK -BLUE = Fore.BLUE -CYAN = Fore.CYAN -GREEN = Fore.GREEN -# MAGENTA = Fore.MAGENTA -RED = Fore.RED -WHITE = Fore.WHITE -# YELLOW = Fore.YELLOW -LIGHT_BLACK = Fore.LIGHTBLACK_EX -LIGHT_BLUE = Fore.LIGHTBLUE_EX -LIGHT_CYAN = Fore.LIGHTCYAN_EX -LIGHT_GREEN = Fore.LIGHTGREEN_EX -LIGHT_MAGENTA = Fore.LIGHTMAGENTA_EX -LIGHT_RED = Fore.LIGHTRED_EX -LIGHT_WHITE = Fore.LIGHTWHITE_EX -LIGHT_YELLOW = Fore.LIGHTYELLOW_EX +if platform.system() == 'Windows': + # pytest-timeout seems to be buggy wrt colorama when capturing output. + # + # TODO: re-enable after enabling proper ANSI sequence handling: + # https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences + + RESET = '' + BLACK = '' + BLUE = '' + CYAN = '' + GREEN = '' + RED = '' + WHITE = '' + LIGHT_BLACK = '' + LIGHT_BLUE = '' + LIGHT_CYAN = '' + LIGHT_GREEN = '' + LIGHT_MAGENTA = '' + LIGHT_RED = '' + LIGHT_WHITE = '' + LIGHT_YELLOW = '' -color_scheme = { - token.Token: ('white', 'white'), - token.Punctuation: ('', ''), - token.Operator: ('', ''), - token.Literal: ('brown', 'brown'), - token.Keyword: ('brown', 'brown'), - token.Name: ('white', 'white'), - token.Name.Constant: ('brown', 'brown'), - token.Name.Attribute: ('brown', 'brown'), - # token.Name.Tag: ('white', 'white'), - # token.Name.Function: ('white', 'white'), - # token.Name.Variable: ('white', 'white'), -} - -formatter = formatters.TerminalFormatter(colorscheme=color_scheme) -json_lexer = lexers.JsonLexer() -python_lexer = lexers.PythonLexer() + def colorize_json(s): + return s -def colorize_json(s): - return highlight(s, json_lexer, formatter).rstrip() + def color_repr(obj): + return repr(obj) -def color_repr(obj): - return highlight(repr(obj), python_lexer, formatter).rstrip() +else: + from colorama import Fore + from pygments import highlight, lexers, formatters, token + + + # Colors that are commented out don't work with PowerShell. + RESET = Fore.RESET + BLACK = Fore.BLACK + BLUE = Fore.BLUE + CYAN = Fore.CYAN + GREEN = Fore.GREEN + # MAGENTA = Fore.MAGENTA + RED = Fore.RED + WHITE = Fore.WHITE + # YELLOW = Fore.YELLOW + LIGHT_BLACK = Fore.LIGHTBLACK_EX + LIGHT_BLUE = Fore.LIGHTBLUE_EX + LIGHT_CYAN = Fore.LIGHTCYAN_EX + LIGHT_GREEN = Fore.LIGHTGREEN_EX + LIGHT_MAGENTA = Fore.LIGHTMAGENTA_EX + LIGHT_RED = Fore.LIGHTRED_EX + LIGHT_WHITE = Fore.LIGHTWHITE_EX + LIGHT_YELLOW = Fore.LIGHTYELLOW_EX + + + color_scheme = { + token.Token: ('white', 'white'), + token.Punctuation: ('', ''), + token.Operator: ('', ''), + token.Literal: ('brown', 'brown'), + token.Keyword: ('brown', 'brown'), + token.Name: ('white', 'white'), + token.Name.Constant: ('brown', 'brown'), + token.Name.Attribute: ('brown', 'brown'), + # token.Name.Tag: ('white', 'white'), + # token.Name.Function: ('white', 'white'), + # token.Name.Variable: ('white', 'white'), + } + + formatter = formatters.TerminalFormatter(colorscheme=color_scheme) + json_lexer = lexers.JsonLexer() + python_lexer = lexers.PythonLexer() + + + def colorize_json(s): + return highlight(s, json_lexer, formatter).rstrip() + + + def color_repr(obj): + return highlight(repr(obj), python_lexer, formatter).rstrip() diff --git a/pytests/helpers/session.py b/pytests/helpers/session.py index 5305afbb..5e07382f 100644 --- a/pytests/helpers/session.py +++ b/pytests/helpers/session.py @@ -20,11 +20,11 @@ from .timeline import Timeline, Event, Response # ptvsd.__file__ will be