Merge remote-tracking branch 'origin/master'

This commit is contained in:
Yann Cébron
2017-03-09 14:50:06 +01:00
57 changed files with 13834 additions and 10328 deletions
@@ -68,6 +68,8 @@ import java.util.*;
*/
public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProvider {
private static final Logger LOG = Logger.getInstance(JavaStackFrame.class);
public static final DummyMessageValueNode LOCAL_VARIABLES_INFO_UNAVAILABLE_MESSAGE_NODE =
new DummyMessageValueNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(), XDebuggerUIConstants.INFORMATION_MESSAGE_ICON);
private final DebugProcessImpl myDebugProcess;
@Nullable private final XSourcePosition myXSourcePosition;
@@ -321,7 +323,7 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
}
catch (EvaluateException e) {
if (e.getCause() instanceof AbsentInformationException) {
children.add(new DummyMessageValueNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(), XDebuggerUIConstants.INFORMATION_MESSAGE_ICON));
children.add(LOCAL_VARIABLES_INFO_UNAVAILABLE_MESSAGE_NODE);
// trying to collect values from variable slots
try {
for (Map.Entry<DecompiledLocalVariable, Value> entry : LocalVariablesUtil.fetchValues(getStackFrameProxy(), debugProcess, true).entrySet()) {
@@ -24,7 +24,6 @@ import com.intellij.debugger.settings.CaptureConfigurable;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.breakpoints.StackCapturingLineBreakpoint;
import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
import com.intellij.debugger.ui.tree.render.ClassRenderer;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.diagnostic.Logger;
@@ -61,9 +60,6 @@ public class StackFrameItem {
private static final List<XNamedValue> VARS_NOT_CAPTURED = Collections.singletonList(
JavaStackFrame.createMessageNode(DebuggerBundle.message("message.node.local.variables.not.captured"),
XDebuggerUIConstants.INFORMATION_MESSAGE_ICON));
private static final XNamedValue VAR_NO_DEBUG_INFO =
JavaStackFrame.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(),
XDebuggerUIConstants.INFORMATION_MESSAGE_ICON);
private final Location myLocation;
private final List<XNamedValue> myVariables;
@@ -125,7 +121,7 @@ public class StackFrameItem {
}
catch (EvaluateException e) {
if (e.getCause() instanceof AbsentInformationException) {
vars.add(VAR_NO_DEBUG_INFO);
vars.add(JavaStackFrame.LOCAL_VARIABLES_INFO_UNAVAILABLE_MESSAGE_NODE);
// only args for frames w/o debug info for now
try {
for (Map.Entry<DecompiledLocalVariable, Value> entry : LocalVariablesUtil
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ object MultiModuleJava9ProjectDescriptor : DefaultLightProjectDescriptor() {
if (this == MAIN) LightPlatformTestCase.getSourceRoot() else TempFileSystem.getInstance().findFileByPath("/$rootName")!!
}
override fun getSdk(): Sdk = IdeaTestUtil.getMockJdk18()
override fun getSdk(): Sdk = IdeaTestUtil.getMockJdk9()
override fun setUpProject(project: Project, handler: SetupHandler) {
super.setUpProject(project, handler)
@@ -338,6 +338,23 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
}
fun `test trailing punctuation`() {
lines_inner {
("X = { };" - "X = { _ };")
(" " - " -- ").default()
(" " - " ").trim()
testAll()
}
// TODO
lines_inner {
("X = { };_" - "X = { _ };_")
(" -- " - " ----").default()
(" -- " - " -- ").trim()
testAll()
}
}
fun `test legacy cases from ByWordTest`() {
lines_inner {
("abc def, 123" - "ab def, 12")
@@ -15,6 +15,7 @@
*/
package com.intellij.util.text;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -87,13 +88,35 @@ public class SemVer implements Comparable<SemVer> {
@Nullable
public static SemVer parseFromText(@NotNull String text) {
return SemVerMatcher.parseFromText(text);
int majorEndInd = text.indexOf('.');
if (majorEndInd < 0) {
return null;
}
int major = StringUtil.parseInt(text.substring(0, majorEndInd), -1);
int minorEndInd = text.indexOf('.', majorEndInd + 1);
if (minorEndInd < 0) {
return null;
}
int minor = StringUtil.parseInt(text.substring(majorEndInd + 1, minorEndInd), -1);
final String patchStr;
int dashInd = text.indexOf('-', minorEndInd + 1);
if (dashInd >= 0) {
patchStr = text.substring(minorEndInd + 1, dashInd);
}
else {
patchStr = text.substring(minorEndInd + 1);
}
int patch = StringUtil.parseInt(patchStr, -1);
if (major >= 0 && minor >= 0 && patch >= 0) {
return new SemVer(text, major, minor, patch);
}
return null;
}
@NotNull
public static SemVer parseFromTextNonNullize(@Nullable final String text) {
if (text == null) return UNKNOWN;
final SemVer ver = SemVerMatcher.parseFromText(text);
final SemVer ver = parseFromText(text);
return ver == null ? UNKNOWN : ver;
}
@@ -1,61 +0,0 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.text;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Irina.Chernushina on 3/9/2017.
*/
public class SemVerMatcher {
@Nullable
public static SemVer parseFromText(@NotNull String text) {
return parseFromText(text, false);
}
@Nullable
public static SemVer parseFromText(@NotNull String text, final boolean allowPartial) {
int majorEndInd = text.indexOf('.');
if (majorEndInd < 0) {
if (!allowPartial) return null;
final int major = StringUtil.parseInt(text, -1);
return major < 0 ? null : new SemVer(text, major, 0, 0);
}
int major = StringUtil.parseInt(text.substring(0, majorEndInd), -1);
int minorEndInd = text.indexOf('.', majorEndInd + 1);
if (minorEndInd < 0) {
if (!allowPartial) return null;
final int minor = StringUtil.parseInt(text.substring(majorEndInd + 1), -1);
return new SemVer(text, major, minor < 0 ? 0 : minor, 0);
}
int minor = StringUtil.parseInt(text.substring(majorEndInd + 1, minorEndInd), -1);
final String patchStr;
int dashInd = text.indexOf('-', minorEndInd + 1);
if (dashInd >= 0) {
patchStr = text.substring(minorEndInd + 1, dashInd);
}
else {
patchStr = text.substring(minorEndInd + 1);
}
int patch = StringUtil.parseInt(patchStr, -1);
if (major >= 0 && minor >= 0 && patch >= 0) {
return new SemVer(text, major, minor, patch);
}
return null;
}
}
@@ -309,7 +309,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
throw new IllegalStateException("No modules found for the target project: " + ideaProject);
}
Collection<IdeaModule> includedModules = exposeCompositeBuild(allModels, projectDataNode);
Collection<IdeaModule> includedModules = exposeCompositeBuild(allModels, resolverCtx, projectDataNode);
final Map<String /* module id */, Pair<DataNode<ModuleData>, IdeaModule>> moduleMap = ContainerUtilRt.newHashMap();
final Map<String /* module id */, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetsMap = ContainerUtil.newHashMap();
projectDataNode.putUserData(RESOLVED_SOURCE_SETS, sourceSetsMap);
@@ -421,8 +421,12 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
@NotNull
private static Collection<IdeaModule> exposeCompositeBuild(ProjectImportAction.AllModels allModels,
DefaultProjectResolverContext resolverCtx,
DataNode<ProjectData> projectDataNode) {
CompositeBuildData compositeBuildData = null;
if(resolverCtx.getSettings() != null && !resolverCtx.getSettings().getExecutionWorkspace().getBuildParticipants().isEmpty()) {
return Collections.emptyList();
}
CompositeBuildData compositeBuildData;
List<IdeaModule> gradleIncludedModules = new SmartList<>();
List<IdeaProject> includedBuilds = allModels.getIncludedBuilds();
if (!includedBuilds.isEmpty()) {
@@ -432,9 +436,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
if (!project.getModules().isEmpty()) {
String rootProjectName = project.getName();
BuildParticipant buildParticipant = new BuildParticipant();
for (IdeaModule ideaModule : project.getModules()) {
gradleIncludedModules.add(ideaModule);
}
gradleIncludedModules.addAll(project.getModules());
GradleProject gradleProject = project.getModules().getAt(0).getGradleProject();
String projectPath = null;
do {
@@ -54,7 +54,9 @@ public class CompositeBuildDataService extends AbstractProjectDataService<Compos
if (projectData != null) {
GradleProjectSettings projectSettings =
GradleSettings.getInstance(project).getLinkedProjectSettings(projectData.getLinkedExternalProjectPath());
if (projectSettings != null) {
if (projectSettings != null &&
projectSettings.getCompositeBuild() != null &&
projectSettings.getCompositeBuild().getCompositeDefinitionSource() == CompositeDefinitionSource.SCRIPT) {
projectSettings.setCompositeBuild(null);
}
}
@@ -21,24 +21,17 @@ def log_error_once(msg):
pydev_src_dir = os.path.dirname(os.path.dirname(__file__))
def _get_pydevd_args():
new_args = []
for x in sys.original_argv:
new_args.append(x)
if x == '--file':
break
return new_args
def _get_python_c_args(host, port, indC, args):
def _get_python_c_args(host, port, indC, args, setup):
host_literal = "'" + host + "'" if host is not None else 'None'
return ("import sys; sys.path.append(r'%s'); import pydevd; "
"pydevd.settrace(host=%s, port=%s, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); "
"sys.original_argv = %s; %s"
"from pydevd import SetupHolder; SetupHolder.setup = %s; %s"
) % (
pydev_src_dir,
host_literal,
port,
_get_pydevd_args(),
setup,
args[indC + 1])
def _get_host_port():
@@ -105,6 +98,7 @@ def patch_args(args):
log_debug("Patching args: %s"% str(args))
args = remove_quotes_from_args(args)
from pydevd import SetupHolder
import sys
new_args = []
i = 0
@@ -122,7 +116,7 @@ def patch_args(args):
if port is not None:
new_args.extend(args)
new_args[indC + 1] = _get_python_c_args(host, port, indC, args)
new_args[indC + 1] = _get_python_c_args(host, port, indC, args, SetupHolder.setup)
return quote_args(new_args)
else:
# Check for Python ZIP Applications and don't patch the args for them.
@@ -155,7 +149,8 @@ def patch_args(args):
# Original args should be something as:
# ['X:\\pysrc\\pydevd.py', '--multiprocess', '--print-in-debugger-startup',
# '--vm_type', 'python', '--client', '127.0.0.1', '--port', '56352', '--file', 'x:\\snippet1.py']
original = sys.original_argv[:]
from _pydevd_bundle.pydevd_command_line_handling import setup_to_argv
original = setup_to_argv(SetupHolder.setup) + ['--file']
while i < len(args):
if args[i] == '-m':
# Always insert at pos == 1 (i.e.: pydevd "--module" --multiprocess ...)
@@ -5,14 +5,20 @@ def set_trace_in_qt():
from _pydevd_bundle.pydevd_comm import get_global_debugger
debugger = get_global_debugger()
if debugger is not None:
pydevd_tracing.SetTrace(debugger.trace_dispatch, debugger.frame_eval_func)
pydevd_tracing.SetTrace(debugger.trace_dispatch)
_patched_qt = False
def patch_qt():
def patch_qt(qt_support_mode):
'''
This method patches qt (PySide, PyQt4, PyQt5) so that we have hooks to set the tracing for QThread.
'''
if not qt_support_mode:
return
if qt_support_mode is True or qt_support_mode == 'True':
# do not break backward compatibility
qt_support_mode = 'auto'
# Avoid patching more than once
global _patched_qt
@@ -21,38 +27,53 @@ def patch_qt():
_patched_qt = True
if qt_support_mode == 'auto':
# Ok, we have an issue here:
# PyDev-452: Selecting PyQT API version using sip.setapi fails in debug mode
# http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html
# Mostly, if the user uses a different API version (i.e.: v2 instead of v1),
# that has to be done before importing PyQt4/5 modules (PySide doesn't have this issue
# as it only implements v2).
patch_qt_on_import = None
try:
import PySide # @UnresolvedImport @UnusedImport
except:
patch_qt_on_import = None
try:
import PyQt4 # @UnresolvedImport @UnusedImport
patch_qt_on_import = 'PyQt4'
import PySide # @UnresolvedImport @UnusedImport
qt_support_mode = 'pyside'
except:
try:
import PyQt5 # @UnresolvedImport @UnusedImport
patch_qt_on_import = 'PyQt5'
qt_support_mode = 'pyqt5'
except:
return
try:
import PyQt4 # @UnresolvedImport @UnusedImport
qt_support_mode = 'pyqt4'
except:
return
if qt_support_mode == 'pyside':
import PySide.QtCore # @UnresolvedImport
_internal_patch_qt(PySide.QtCore)
elif qt_support_mode == 'pyqt5':
import PyQt5.QtCore # @UnresolvedImport
_internal_patch_qt(PyQt5.QtCore)
elif qt_support_mode == 'pyqt4':
# Ok, we have an issue here:
# PyDev-452: Selecting PyQT API version using sip.setapi fails in debug mode
# http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html
# Mostly, if the user uses a different API version (i.e.: v2 instead of v1),
# that has to be done before importing PyQt4 modules (PySide/PyQt5 don't have this issue
# as they only implements v2).
patch_qt_on_import = 'PyQt4'
def get_qt_core_module():
import PyQt4.QtCore # @UnresolvedImport
return PyQt4.QtCore
_patch_import_to_patch_pyqt_on_import(patch_qt_on_import, get_qt_core_module)
if patch_qt_on_import:
_patch_import_to_patch_pyqt_on_import(patch_qt_on_import)
else:
_internal_patch_qt()
raise ValueError('Unexpected qt support mode: %s' % (qt_support_mode,))
def _patch_import_to_patch_pyqt_on_import(patch_qt_on_import):
def _patch_import_to_patch_pyqt_on_import(patch_qt_on_import, get_qt_core_module):
# I don't like this approach very much as we have to patch __import__, but I like even less
# asking the user to configure something in the client side...
# So, our approach is to patch PyQt4/5 right before the user tries to import it (at which
# So, our approach is to patch PyQt4 right before the user tries to import it (at which
# point he should've set the sip api version properly already anyways).
dotted = patch_qt_on_import + '.'
@@ -67,7 +88,7 @@ def _patch_import_to_patch_pyqt_on_import(patch_qt_on_import):
if patch_qt_on_import == name or name.startswith(dotted):
builtins.__import__ = original_import
cancel_patches_in_sys_module()
_internal_patch_qt() # Patch it only when the user would import the qt module
_internal_patch_qt(get_qt_core_module()) # Patch it only when the user would import the qt module
return original_import(name, *args, **kwargs)
try:
@@ -77,47 +98,36 @@ def _patch_import_to_patch_pyqt_on_import(patch_qt_on_import):
builtins.__import__ = patched_import
def _internal_patch_qt():
try:
from PySide import QtCore # @UnresolvedImport @UnusedImport
except:
try:
from PyQt4 import QtCore # @UnresolvedImport @UnusedImport @Reimport
except:
try:
from PyQt5 import QtCore # @UnresolvedImport @UnusedImport @Reimport
except:
return
def _internal_patch_qt(QtCore):
_original_thread_init = QtCore.QThread.__init__
_original_runnable_init = QtCore.QRunnable.__init__
_original_QThread = QtCore.QThread
class StartedSignalWrapper(QtCore.QObject): # Wrapper for the QThread.started signal
class FuncWrapper:
def __init__(self, original):
self._original = original
def __call__(self, *args, **kwargs):
set_trace_in_qt()
return self._original(*args, **kwargs)
class StartedSignalWrapper: # Wrapper for the QThread.started signal
try:
_signal = QtCore.Signal() # @UndefinedVariable
except:
_signal = QtCore.pyqtSignal() # @UndefinedVariable
def __init__(self, thread, original_started):
QtCore.QObject.__init__(self)
self.thread = thread
self.original_started = original_started
self._signal.connect(self._on_call)
self.original_started.connect(self._signal)
def connect(self, func, *args, **kwargs):
return self.original_started.connect(FuncWrapper(func), *args, **kwargs)
def connect(self, *args, **kwargs):
return self._signal.connect(*args, **kwargs)
def disconnect(self, *args, **kwargs):
return self.original_started.disconnect(*args, **kwargs)
return self._signal.disconnect(*args, **kwargs)
def emit(self, *args, **kwargs):
return self.original_started.emit(*args, **kwargs)
return self._signal.emit(*args, **kwargs)
def _on_call(self, *args, **kwargs):
set_trace_in_qt()
class ThreadWrapper(QtCore.QThread): # Wrapper for QThread
@@ -1,39 +1,61 @@
import pickle, zlib, base64, os
import py
from _pydev_runfiles import pydev_runfiles_xml_rpc
import pickle
import zlib
import base64
import os
import py
from pydevd_file_utils import _NormFile
import pytest
import sys
import time
#===================================================================================================
#=========================================================================
# Load filters with tests we should skip
#===================================================================================================
#=========================================================================
py_test_accept_filter = None
def _load_filters():
global py_test_accept_filter
if py_test_accept_filter is None:
py_test_accept_filter = os.environ.get('PYDEV_PYTEST_SKIP')
if py_test_accept_filter:
py_test_accept_filter = pickle.loads(zlib.decompress(base64.b64decode(py_test_accept_filter)))
py_test_accept_filter = pickle.loads(
zlib.decompress(base64.b64decode(py_test_accept_filter)))
else:
py_test_accept_filter = {}
def connect_to_server_for_communication_to_xml_rpc_on_xdist():
def is_in_xdist_node():
main_pid = os.environ.get('PYDEV_MAIN_PID')
if main_pid and main_pid != str(os.getpid()):
return True
return False
connected = False
def connect_to_server_for_communication_to_xml_rpc_on_xdist():
global connected
if connected:
return
connected = True
if is_in_xdist_node():
port = os.environ.get('PYDEV_PYTEST_SERVER')
if not port:
sys.stderr.write('Error: no PYDEV_PYTEST_SERVER environment variable defined.\n')
sys.stderr.write(
'Error: no PYDEV_PYTEST_SERVER environment variable defined.\n')
else:
pydev_runfiles_xml_rpc.initialize_server(int(port), daemon=True)
PY2 = sys.version_info[0] <= 2
PY3 = not PY2
#=========================================================================
# Mocking to get clickable file representations
#=========================================================================
_mock_code = []
try:
from py._code import code # @UnresolvedImport
@@ -46,14 +68,10 @@ try:
except ImportError:
pass
#===================================================================================================
# Mocking to get clickable file representations
#===================================================================================================
def _MockFileRepresentation():
for code in _mock_code:
code.ReprFileLocation._original_toterminal = code.ReprFileLocation.toterminal
def toterminal(self, tw):
# filename and lineno output for each entry,
# using an output format that most editors understand
@@ -65,12 +83,15 @@ def _MockFileRepresentation():
path = os.path.abspath(self.path)
if PY2:
if not isinstance(path, unicode): # Note: it usually is NOT unicode...
# Note: it usually is NOT unicode...
if not isinstance(path, unicode):
path = path.decode(sys.getfilesystemencoding(), 'replace')
if not isinstance(msg, unicode): # Note: it usually is unicode...
# Note: it usually is unicode...
if not isinstance(msg, unicode):
msg = msg.decode('utf-8', 'replace')
unicode_line = unicode('File "%s", line %s\n%s') % (path, self.lineno, msg)
unicode_line = unicode('File "%s", line %s\n%s') % (
path, self.lineno, msg)
tw.line(unicode_line)
else:
tw.line('File "%s", line %s\n%s' % (path, self.lineno, msg))
@@ -80,209 +101,238 @@ def _MockFileRepresentation():
def _UninstallMockFileRepresentation():
for code in _mock_code:
code.ReprFileLocation.toterminal = code.ReprFileLocation._original_toterminal #@UndefinedVariable
# @UndefinedVariable
code.ReprFileLocation.toterminal = code.ReprFileLocation._original_toterminal
#=========================================================================
# End mocking to get clickable file representations
#=========================================================================
class State:
numcollected = 0
start_time = time.time()
buf_err = None
buf_out = None
def pytest_configure(*args, **kwargs):
def start_redirect():
if State.buf_out is not None:
return
from _pydevd_bundle import pydevd_io
State.buf_err = pydevd_io.start_redirect(keep_original_redirection=True, std='stderr')
State.buf_out = pydevd_io.start_redirect(keep_original_redirection=True, std='stdout')
def get_curr_output():
return State.buf_out.getvalue(), State.buf_err.getvalue()
def pytest_configure():
_MockFileRepresentation()
def pytest_collectreport(report):
i = 0
for x in report.result:
if isinstance(x, pytest.Item):
try:
# Call our setup (which may do a skip, in which
# case we won't count it).
pytest_runtest_setup(x)
i += 1
except:
continue
State.numcollected += i
def pytest_collection_modifyitems():
connect_to_server_for_communication_to_xml_rpc_on_xdist()
pydev_runfiles_xml_rpc.notifyTestsCollected(State.numcollected)
State.numcollected = 0
def pytest_unconfigure(*args, **kwargs):
def pytest_unconfigure():
_UninstallMockFileRepresentation()
pydev_runfiles_xml_rpc.notifyTestRunFinished('Finished in: %.2f secs.' % (time.time() - State.start_time,))
if is_in_xdist_node():
return
# Only report that it finished when on the main node (we don't want to report
# the finish on each separate node).
pydev_runfiles_xml_rpc.notifyTestRunFinished(
'Finished in: %.2f secs.' % (time.time() - State.start_time,))
def pytest_runtest_setup(item):
filename = item.fspath.strpath
test = item.location[2]
State.start_test_time = time.time()
def pytest_collection_modifyitems(session, config, items):
# A note: in xdist, this is not called on the main process, only in the
# secondary nodes, so, we'll actually make the filter and report it multiple
# times.
connect_to_server_for_communication_to_xml_rpc_on_xdist()
pydev_runfiles_xml_rpc.notifyStartTest(filename, test)
_load_filters()
if not py_test_accept_filter:
pydev_runfiles_xml_rpc.notifyTestsCollected(len(items))
return # Keep on going (nothing to filter)
new_items = []
for item in items:
f = _NormFile(str(item.parent.fspath))
name = item.name
def report_test(cond, filename, test, captured_output, error_contents, delta):
'''
@param filename: 'D:\\src\\mod1\\hello.py'
@param test: 'TestCase.testMet1'
@param cond: fail, error, ok
'''
time_str = '%.2f' % (delta,)
pydev_runfiles_xml_rpc.notifyTest(cond, captured_output, error_contents, filename, test, time_str)
if f not in py_test_accept_filter:
# print('Skip file: %s' % (f,))
continue # Skip the file
accept_tests = py_test_accept_filter[f]
def pytest_runtest_makereport(item, call):
report_when = call.when
report_duration = call.stop-call.start
excinfo = call.excinfo
if not call.excinfo:
evalxfail = getattr(item, '_evalxfail', None)
if evalxfail and report_when == 'call' and (not hasattr(evalxfail, 'expr') or evalxfail.expr):
# I.e.: a method marked with xfail passed... let the user know.
report_outcome = "failed"
report_longrepr = "XFAIL: Unexpected pass"
if item.cls is not None:
class_name = item.cls.__name__
else:
report_outcome = "passed"
report_longrepr = None
else:
excinfo = call.excinfo
class_name = None
for test in accept_tests:
# This happens when parameterizing pytest tests.
i = name.find('[')
if i > 0:
name = name[:i]
if test == name:
# Direct match of the test (just go on with the default
# loading)
new_items.append(item)
break
handled = False
if class_name is not None:
if test == class_name + '.' + name:
new_items.append(item)
break
if not (call.excinfo and
call.excinfo.errisinstance(pytest.xfail.Exception)):
evalxfail = getattr(item, '_evalxfail', None)
# Something which had an xfail failed: this is expected.
if evalxfail and (not hasattr(evalxfail, 'expr') or evalxfail.expr):
report_outcome = "passed"
report_longrepr = None
handled = True
if handled:
if class_name == test:
new_items.append(item)
break
else:
pass
# print('Skip test: %s.%s. Accept: %s' % (class_name, name, accept_tests))
if excinfo.errisinstance(pytest.xfail.Exception):
# Case where an explicit xfail is raised (i.e.: pytest.xfail("reason") is called
# programatically).
report_outcome = "passed"
report_longrepr = None
# Modify the original list
items[:] = new_items
pydev_runfiles_xml_rpc.notifyTestsCollected(len(items))
elif excinfo.errisinstance(py.test.skip.Exception): # @UndefinedVariable
report_outcome = "skipped"
r = excinfo._getreprcrash()
report_longrepr = None #(str(r.path), r.lineno, r.message)
elif not isinstance(excinfo, py.code.ExceptionInfo): # @UndefinedVariable
report_outcome = "failed"
report_longrepr = excinfo
from py.io import TerminalWriter
else:
report_outcome = "failed"
if call.when == "call":
report_longrepr = item.repr_failure(excinfo)
def _get_error_contents_from_report(report):
if report.longrepr is not None:
tw = TerminalWriter(stringio=True)
tw.hasmarkup = False
report.toterminal(tw)
exc = tw.stringio.getvalue()
s = exc.strip()
if s:
return s
else: # exception in setup or teardown
report_longrepr = item._repr_failure_py(excinfo, style=item.config.option.tbstyle)
return ''
filename = item.fspath.strpath
test = item.location[2]
def pytest_collectreport(report):
error_contents = _get_error_contents_from_report(report)
if error_contents:
report_test('fail', '<collect errors>', '<collect errors>', '', error_contents, 0.0)
status = 'ok'
captured_output = ''
error_contents = ''
def append_strings(s1, s2):
if s1.__class__ == s2.__class__:
return s1 + s2
if report_outcome in ('passed', 'skipped'):
#passed or skipped: no need to report if in setup or teardown (only on the actual test if it passed).
if sys.version_info[0] == 2:
if not isinstance(s1, basestring):
s1 = str(s1)
if not isinstance(s2, basestring):
s2 = str(s2)
# Prefer bytes
if isinstance(s1, unicode):
s1 = s1.encode('utf-8')
if isinstance(s2, unicode):
s2 = s2.encode('utf-8')
return s1 + s2
else:
# Prefer str
if isinstance(s1, bytes):
s1 = s1.decode('utf-8', 'replace')
if isinstance(s2, bytes):
s2 = s2.decode('utf-8', 'replace')
return s1 + s2
def pytest_runtest_logreport(report):
if is_in_xdist_node():
# When running with xdist, we don't want the report to be called from the node, only
# from the main process.
return
report_duration = report.duration
report_when = report.when
report_outcome = report.outcome
if hasattr(report, 'wasxfail'):
if report_outcome != 'skipped':
report_outcome = 'passed'
if report_outcome == 'passed':
# passed on setup/teardown: no need to report if in setup or teardown
# (only on the actual test if it passed).
if report_when in ('setup', 'teardown'):
return
else:
#It has only passed, skipped and failed (no error), so, let's consider error if not on call.
if report_when == 'setup':
if status == 'ok':
status = 'error'
status = 'ok'
elif report_when == 'teardown':
if status == 'ok':
status = 'error'
elif report_outcome == 'skipped':
status = 'skip'
else:
# It has only passed, skipped and failed (no error), so, let's consider
# error if not on call.
if report_when in ('setup', 'teardown'):
status = 'error'
else:
#any error in the call (not in setup or teardown) is considered a regular failure.
# any error in the call (not in setup or teardown) is considered a
# regular failure.
status = 'fail'
# This will work if pytest is not capturing it, if it is, nothing will
# come from here...
captured_output, error_contents = report.pydev_captured_output, report.pydev_error_contents
for type_section, value in report.sections:
if value:
if type_section in ('err', 'stderr', 'Captured stderr call'):
error_contents = append_strings(error_contents, value)
else:
captured_output = append_strings(error_contents, value)
if call.excinfo:
rep = report_longrepr
if hasattr(rep, 'reprcrash'):
reprcrash = rep.reprcrash
error_contents += str(reprcrash)
error_contents += '\n'
filename = report.pydev_fspath_strpath
test = report.location[2]
if hasattr(rep, 'reprtraceback'):
error_contents += str(rep.reprtraceback)
if report_outcome != 'skipped':
# On skipped, we'll have a traceback for the skip, which is not what we
# want.
exc = _get_error_contents_from_report(report)
if exc:
if error_contents:
error_contents = append_strings(error_contents, '----------------------------- Exceptions -----------------------------\n')
error_contents = append_strings(error_contents, exc)
if hasattr(rep, 'sections'):
for name, content, sep in rep.sections:
error_contents += sep * 40
error_contents += name
error_contents += sep * 40
error_contents += '\n'
error_contents += content
error_contents += '\n'
else:
if report_longrepr:
error_contents += str(report_longrepr)
report_test(status, filename, test, captured_output, error_contents, report_duration)
if status != 'skip': #I.e.: don't event report skips...
report_test(status, filename, test, captured_output, error_contents, report_duration)
def report_test(status, filename, test, captured_output, error_contents, duration):
'''
@param filename: 'D:\\src\\mod1\\hello.py'
@param test: 'TestCase.testMet1'
@param status: fail, error, ok
'''
time_str = '%.2f' % (duration,)
pydev_runfiles_xml_rpc.notifyTest(
status, captured_output, error_contents, filename, test, time_str)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
report.pydev_fspath_strpath = item.fspath.strpath
report.pydev_captured_output, report.pydev_error_contents = get_curr_output()
@pytest.mark.tryfirst
def pytest_runtest_setup(item): # @DuplicatedSignature
def pytest_runtest_setup(item):
'''
Skips tests. With xdist will be on a secondary process.
Note: with xdist will be on a secondary process.
'''
_load_filters()
if not py_test_accept_filter:
return #Keep on going (nothing to filter)
f = _NormFile(str(item.parent.fspath))
name = item.name
if f not in py_test_accept_filter:
pytest.skip() # Skip the file
accept_tests = py_test_accept_filter[f]
if item.cls is not None:
class_name = item.cls.__name__
else:
class_name = None
for test in accept_tests:
# This happens when parameterizing pytest tests.
i = name.find('[')
if i > 0:
name = name[:i]
if test == name:
#Direct match of the test (just go on with the default loading)
return
if class_name is not None:
if test == class_name + '.' + name:
return
if class_name == test:
return
# If we had a match it'd have returned already.
pytest.skip() # Skip the test
# We have our own redirection: if xdist does its redirection, we'll have
# nothing in our contents (which is OK), but if it does, we'll get nothing
# from pytest but will get our own here.
start_redirect()
filename = item.fspath.strpath
test = item.location[2]
pydev_runfiles_xml_rpc.notifyStartTest(filename, test)
@@ -26,6 +26,17 @@ _PythonTextTestResult = python_unittest.TextTestRunner()._makeResult().__class__
#=======================================================================================================================
class PydevTestResult(_PythonTextTestResult):
def addSubTest(self, test, subtest, err):
"""Called at the end of a subtest.
'err' is None if the subtest ended successfully, otherwise it's a
tuple of values as returned by sys.exc_info().
"""
_PythonTextTestResult.addSubTest(self, test, subtest, err)
if err is not None:
subdesc = subtest._subDescription()
error = (test, self._exc_info_to_string(err, test))
self._reportErrors([error], [], '', '%s %s' % (self.get_test_name(test), subdesc))
def startTest(self, test):
_PythonTextTestResult.startTest(self, test)
@@ -1,8 +1,9 @@
import sys
import weakref
from _pydev_imps._pydev_saved_modules import thread
from _pydevd_bundle.pydevd_constants import STATE_RUN, PYTHON_SUSPEND, dict_iter_items
from _pydevd_bundle.pydevd_constants import STATE_RUN, PYTHON_SUSPEND
# IFDEF CYTHON
# ELSE
from _pydevd_bundle.pydevd_frame import PyDBFrame
# ENDIF
#=======================================================================================================================
@@ -84,110 +85,3 @@ class PyDBAdditionalThreadInfo(object):
def __str__(self):
return 'State:%s Stop:%s Cmd: %s Kill:%s' % (
self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill)
#=======================================================================================================================
# Note that the Cython version has only the contents above
#=======================================================================================================================
# IFDEF CYTHON
# ELSE
PyDBAdditionalThreadInfoOriginal = PyDBAdditionalThreadInfo
#=======================================================================================================================
# PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
#=======================================================================================================================
class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(PyDBAdditionalThreadInfoOriginal):
def __init__(self):
PyDBAdditionalThreadInfoOriginal.__init__(self)
#That's where the last frame entered is kept. That's needed so that we're able to
#trace contexts that were previously untraced and are currently active. So, the bad thing
#is that the frame may be kept alive longer than it would if we go up on the frame stack,
#and is only disposed when some other frame is removed.
#A better way would be if we could get the topmost frame for each thread, but that's
#not possible (until python 2.5 -- which is the PyDBAdditionalThreadInfo version)
#Or if the user compiled threadframe (from http://www.majid.info/mylos/stories/2004/06/10/threadframe.html)
#NOT RLock!! (could deadlock if it was)
self.lock = thread.allocate_lock()
self._acquire_lock = self.lock.acquire
self._release_lock = self.lock.release
#collection with the refs
d = {}
self.pydev_existing_frames = d
try:
self._iter_frames = d.iterkeys
except AttributeError:
self._iter_frames = d.keys
def _OnDbFrameCollected(self, ref):
'''
Callback to be called when a given reference is garbage-collected.
'''
self._acquire_lock()
try:
del self.pydev_existing_frames[ref]
finally:
self._release_lock()
def _AddDbFrame(self, db_frame):
self._acquire_lock()
try:
#create the db frame with a callback to remove it from the dict when it's garbage-collected
#(could be a set, but that's not available on all versions we want to target).
r = weakref.ref(db_frame, self._OnDbFrameCollected)
self.pydev_existing_frames[r] = r
finally:
self._release_lock()
def create_db_frame(self, args):
#the frame must be cached as a weak-ref (we return the actual db frame -- which will be kept
#alive until its trace_dispatch method is not referenced anymore).
#that's a large workaround because:
#1. we can't have weak-references to python frame object
#2. only from 2.5 onwards we have _current_frames support from the interpreter
db_frame = PyDBFrame(args)
db_frame.frame = args[-1]
self._AddDbFrame(db_frame)
return db_frame
def iter_frames(self, t):
#We cannot use yield (because of the lock)
self._acquire_lock()
try:
ret = []
for weak_db_frame in self._iter_frames():
try:
ret.append(weak_db_frame().frame)
except AttributeError:
pass # ok, garbage-collected already
return ret
finally:
self._release_lock()
def __str__(self):
return 'State:%s Stop:%s Cmd: %s Kill:%s Frames:%s' % (
self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill, len(self.iter_frames(None)))
#=======================================================================================================================
# NOW, WE HAVE TO DEFINE WHICH THREAD INFO TO USE
# (whether we have to keep references to the frames or not)
# from version 2.5 onwards, we can use sys._current_frames to get a dict with the threads
# and frames, but to support other versions, we can't rely on that.
#=======================================================================================================================
if not hasattr(sys, '_current_frames'):
try:
import threadframe #@UnresolvedImport
sys._current_frames = threadframe.dict
assert sys._current_frames is threadframe.dict #Just check if it was correctly set
except:
#If all fails, let's use the support without frames
PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
# ENDIF
@@ -0,0 +1,145 @@
class ArgHandlerWithParam:
'''
Handler for some arguments which needs a value
'''
def __init__(self, arg_name, convert_val=None, default_val=None):
self.arg_name = arg_name
self.arg_v_rep = '--%s' % (arg_name,)
self.convert_val = convert_val
self.default_val = default_val
def to_argv(self, lst, setup):
v = setup.get(self.arg_name)
if v is not None and v != self.default_val:
lst.append(self.arg_v_rep)
lst.append('%s' % (v,))
def handle_argv(self, argv, i, setup):
assert argv[i] == self.arg_v_rep
del argv[i]
val = argv[i]
if self.convert_val:
val = self.convert_val(val)
setup[self.arg_name] = val
del argv[i]
class ArgHandlerBool:
'''
If a given flag is received, mark it as 'True' in setup.
'''
def __init__(self, arg_name, default_val=False):
self.arg_name = arg_name
self.arg_v_rep = '--%s' % (arg_name,)
self.default_val = default_val
def to_argv(self, lst, setup):
v = setup.get(self.arg_name)
if v:
lst.append(self.arg_v_rep)
def handle_argv(self, argv, i, setup):
assert argv[i] == self.arg_v_rep
del argv[i]
setup[self.arg_name] = True
ACCEPTED_ARG_HANDLERS = [
ArgHandlerWithParam('port', int, 0),
ArgHandlerWithParam('vm_type'),
ArgHandlerWithParam('client'),
ArgHandlerBool('server'),
ArgHandlerBool('DEBUG_RECORD_SOCKET_READS'),
ArgHandlerBool('multiproc'), # Used by PyCharm (reuses connection: ssh tunneling)
ArgHandlerBool('multiprocess'), # Used by PyDev (creates new connection to ide)
ArgHandlerBool('save-signatures'),
ArgHandlerBool('save-threading'),
ArgHandlerBool('save-asyncio'),
ArgHandlerBool('print-in-debugger-startup'),
ArgHandlerBool('cmd-line'),
ArgHandlerBool('module'),
]
ARGV_REP_TO_HANDLER = {}
for handler in ACCEPTED_ARG_HANDLERS:
ARGV_REP_TO_HANDLER[handler.arg_v_rep] = handler
def get_pydevd_file():
import pydevd
f = pydevd.__file__
if f.endswith('.pyc'):
f = f[:-1]
return f
def setup_to_argv(setup):
'''
:param dict setup:
A dict previously gotten from process_command_line.
:note: does not handle --file nor --DEBUG.
'''
ret = [get_pydevd_file()]
for handler in ACCEPTED_ARG_HANDLERS:
if handler.arg_name in setup:
handler.to_argv(ret, setup)
return ret
def process_command_line(argv):
""" parses the arguments.
removes our arguments from the command line """
setup = {}
for handler in ACCEPTED_ARG_HANDLERS:
setup[handler.arg_name] = handler.default_val
setup['file'] = ''
setup['qt-support'] = ''
i = 0
del argv[0]
while i < len(argv):
handler = ARGV_REP_TO_HANDLER.get(argv[i])
if handler is not None:
handler.handle_argv(argv, i, setup)
elif argv[i].startswith('--qt-support'):
# The --qt-support is special because we want to keep backward compatibility:
# Previously, just passing '--qt-support' meant that we should use the auto-discovery mode
# whereas now, if --qt-support is passed, it should be passed as --qt-support=<mode>, where
# mode can be one of 'auto', 'none', 'pyqt5', 'pyqt4', 'pyside'.
if argv[i] == '--qt-support':
setup['qt-support'] = 'auto'
elif argv[i].startswith('--qt-support='):
qt_support = argv[i][len('--qt-support='):]
valid_modes = ('none', 'auto', 'pyqt5', 'pyqt4', 'pyside')
if qt_support not in valid_modes:
raise ValueError("qt-support mode invalid: " + qt_support)
if qt_support == 'none':
# On none, actually set an empty string to evaluate to False.
setup['qt-support'] = ''
else:
setup['qt-support'] = qt_support
else:
raise ValueError("Unexpected definition for qt-support flag: " + argv[i])
del argv[i]
elif argv[i] == '--file':
# --file is special because it's the last one (so, no handler for it).
del argv[i]
setup['file'] = argv[i]
i = len(argv) # pop out, file is our last argument
elif argv[i] == '--DEBUG':
from pydevd import set_debug
del argv[i]
set_debug(setup)
else:
raise ValueError("Unexpected option: " + argv[i])
return setup
@@ -157,20 +157,6 @@ except:
except NameError:
def dict_contains(d, key):
return d.has_key(key)
try:
dict_pop = dict.pop
except:
#=======================================================================================================================
# Jython 2.1
#=======================================================================================================================
def dict_pop(d, key, default=None):
try:
ret = d[key]
del d[key]
return ret
except:
return default
if IS_PY3K:
def dict_keys(d):
@@ -1,4 +1,4 @@
from _pydevd_bundle.pydevd_constants import dict_pop, get_thread_id, Null
from _pydevd_bundle.pydevd_constants import get_thread_id, Null
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
from _pydev_imps._pydev_saved_modules import thread, threading
import sys
@@ -125,7 +125,7 @@ def remove_custom_frame(frame_id):
try:
if DEBUG:
sys.stderr.write('remove_custom_frame: %s\n' % frame_id)
dict_pop(CustomFramesContainer.custom_frames, frame_id, None)
CustomFramesContainer.custom_frames.pop(frame_id, None)
CustomFramesContainer._py_db_command_thread_event.set()
finally:
CustomFramesContainer.custom_frames_lock.release()
File diff suppressed because it is too large Load Diff
@@ -3,10 +3,11 @@
# DO NOT edit manually!
# DO NOT edit manually!
import sys
import weakref
from _pydev_imps._pydev_saved_modules import thread
from _pydevd_bundle.pydevd_constants import STATE_RUN, PYTHON_SUSPEND, dict_iter_items
from _pydevd_bundle.pydevd_frame import PyDBFrame
from _pydevd_bundle.pydevd_constants import STATE_RUN, PYTHON_SUSPEND
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
# ELSE
# from _pydevd_bundle.pydevd_frame import PyDBFrame
# ENDIF
#=======================================================================================================================
@@ -88,113 +89,6 @@ cdef class PyDBAdditionalThreadInfo:
def __str__(self):
return 'State:%s Stop:%s Cmd: %s Kill:%s' % (
self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill)
#=======================================================================================================================
# Note that the Cython version has only the contents above
#=======================================================================================================================
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
# ELSE
#
# PyDBAdditionalThreadInfoOriginal = PyDBAdditionalThreadInfo
# #=======================================================================================================================
# # PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
# #=======================================================================================================================
# class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(PyDBAdditionalThreadInfoOriginal):
#
# def __init__(self):
# PyDBAdditionalThreadInfoOriginal.__init__(self)
# #That's where the last frame entered is kept. That's needed so that we're able to
# #trace contexts that were previously untraced and are currently active. So, the bad thing
# #is that the frame may be kept alive longer than it would if we go up on the frame stack,
# #and is only disposed when some other frame is removed.
# #A better way would be if we could get the topmost frame for each thread, but that's
# #not possible (until python 2.5 -- which is the PyDBAdditionalThreadInfo version)
# #Or if the user compiled threadframe (from http://www.majid.info/mylos/stories/2004/06/10/threadframe.html)
#
# #NOT RLock!! (could deadlock if it was)
# self.lock = thread.allocate_lock()
# self._acquire_lock = self.lock.acquire
# self._release_lock = self.lock.release
#
# #collection with the refs
# d = {}
# self.pydev_existing_frames = d
# try:
# self._iter_frames = d.iterkeys
# except AttributeError:
# self._iter_frames = d.keys
#
#
# def _OnDbFrameCollected(self, ref):
# '''
# Callback to be called when a given reference is garbage-collected.
# '''
# self._acquire_lock()
# try:
# del self.pydev_existing_frames[ref]
# finally:
# self._release_lock()
#
#
# def _AddDbFrame(self, db_frame):
# self._acquire_lock()
# try:
# #create the db frame with a callback to remove it from the dict when it's garbage-collected
# #(could be a set, but that's not available on all versions we want to target).
# r = weakref.ref(db_frame, self._OnDbFrameCollected)
# self.pydev_existing_frames[r] = r
# finally:
# self._release_lock()
#
#
# def create_db_frame(self, args):
# #the frame must be cached as a weak-ref (we return the actual db frame -- which will be kept
# #alive until its trace_dispatch method is not referenced anymore).
# #that's a large workaround because:
# #1. we can't have weak-references to python frame object
# #2. only from 2.5 onwards we have _current_frames support from the interpreter
# db_frame = PyDBFrame(args)
# db_frame.frame = args[-1]
# self._AddDbFrame(db_frame)
# return db_frame
#
#
# def iter_frames(self, t):
# #We cannot use yield (because of the lock)
# self._acquire_lock()
# try:
# ret = []
#
# for weak_db_frame in self._iter_frames():
# try:
# ret.append(weak_db_frame().frame)
# except AttributeError:
# pass # ok, garbage-collected already
# return ret
# finally:
# self._release_lock()
#
# def __str__(self):
# return 'State:%s Stop:%s Cmd: %s Kill:%s Frames:%s' % (
# self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill, len(self.iter_frames(None)))
#
# #=======================================================================================================================
# # NOW, WE HAVE TO DEFINE WHICH THREAD INFO TO USE
# # (whether we have to keep references to the frames or not)
# # from version 2.5 onwards, we can use sys._current_frames to get a dict with the threads
# # and frames, but to support other versions, we can't rely on that.
# #=======================================================================================================================
# if not hasattr(sys, '_current_frames'):
# try:
# import threadframe #@UnresolvedImport
# sys._current_frames = threadframe.dict
# assert sys._current_frames is threadframe.dict #Just check if it was correctly set
# except:
# #If all fails, let's use the support without frames
# PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
#
# ENDIF
import linecache
import os.path
import re
@@ -208,7 +102,7 @@ from _pydevd_bundle.pydevd_breakpoints import get_exception_breakpoint
from _pydevd_bundle.pydevd_comm import CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_BREAK, \
CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT, CMD_STEP_INTO_MY_CODE
from _pydevd_bundle.pydevd_constants import STATE_SUSPEND, dict_contains, get_thread_id, STATE_RUN, dict_iter_values, IS_PY3K, \
dict_keys, dict_pop, RETURN_VALUES_DICT
dict_keys, RETURN_VALUES_DICT
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
@@ -237,11 +131,16 @@ get_file_type = DONT_TRACE.get
#=======================================================================================================================
# PyDBFrame
#=======================================================================================================================
class PyDBFrame: # No longer cdef because object was dying when only a reference to trace_dispatch was kept (need to check alternatives).
'''This makes the tracing for a given frame, so, the trace_dispatch
is used initially when we enter into a new context ('call') and then
is reused for the entire context.
'''
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
cdef class PyDBFrame:
# ELSE
# class PyDBFrame:
# '''This makes the tracing for a given frame, so, the trace_dispatch
# is used initially when we enter into a new context ('call') and then
# is reused for the entire context.
# '''
# ENDIF
#Note: class (and not instance) attributes.
@@ -249,16 +148,20 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
#considers the user input (so, the actual result must be a join of both).
filename_to_lines_where_exceptions_are_ignored = {}
filename_to_stat_info = {}
should_skip = -1
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
def __init__(self, args):
cdef tuple _args
cdef int should_skip
def __init__(self, tuple args):
self._args = args # In the cython version we don't need to pass the frame
self.should_skip = -1 # On cythonized version, put in instance.
# ELSE
# should_skip = -1 # Default value in class (put in instance on set).
#
# def __init__(self, args):
# #args = main_debugger, filename, base, info, t, frame
# #yeap, much faster than putting in self and then getting it from self later on
# self._args = args[:-1] # Remove the frame (we don't want to have a reference to it).
# self._args = args
# ENDIF
def set_suspend(self, *args, **kwargs):
@@ -301,7 +204,8 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
info = self._args[2]
flag = False
if info.pydev_state != STATE_SUSPEND: #and breakpoint is not None:
# STATE_SUSPEND = 2
if info.pydev_state != 2: #and breakpoint is not None:
exception, value, trace = arg
if trace is not None: #on jython trace is None on the first event
@@ -336,7 +240,7 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if main_debugger.plugin is not None:
result = main_debugger.plugin.exception_break(main_debugger, self, frame, self._args, arg)
if result:
(flag, frame) = result
flag, frame = result
except:
flag = False
@@ -463,6 +367,19 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
main_debugger = None
thread = None
def get_func_name(self, frame):
code_obj = frame.f_code
func_name = code_obj.co_name
try:
cls_name = get_clsname_for_code(code_obj, frame)
if cls_name is not None:
return "%s.%s" % (cls_name, func_name)
else:
return func_name
except:
traceback.print_exc()
return func_name
def manage_return_values(self, main_debugger, frame, event, arg):
def get_func_name(frame):
@@ -490,17 +407,17 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
# Showing return values was turned off, we should remove them from locals dict.
# The values can be in the current frame or in the back one
if RETURN_VALUES_DICT in dict_keys(frame.f_locals):
dict_pop(frame.f_locals, RETURN_VALUES_DICT)
frame.f_locals.pop(RETURN_VALUES_DICT)
if hasattr(frame, "f_back") and hasattr(frame.f_back, "f_locals"):
if RETURN_VALUES_DICT in dict_keys(frame.f_back.f_locals):
dict_pop(frame.f_back.f_locals, RETURN_VALUES_DICT)
frame.f_back.f_locals.pop(RETURN_VALUES_DICT)
main_debugger.remove_return_values_flag = False
except:
main_debugger.remove_return_values_flag = False
traceback.print_exc()
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
def trace_dispatch(self, frame, str event, arg):
cpdef trace_dispatch(self, frame, str event, arg):
cdef str filename;
cdef bint is_exception_event;
cdef bint has_exception_breakpoints;
@@ -508,25 +425,30 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
cdef PyDBAdditionalThreadInfo info;
cdef int step_cmd;
cdef int line;
cdef bint is_line;
cdef bint is_call;
cdef bint is_return;
cdef str curr_func_name;
cdef bint exist_result;
cdef dict frame_skips_cache;
cdef tuple frame_cache_key;
cdef tuple line_cache_key;
cdef int breakpoints_in_line_cache;
cdef int breakpoints_in_frame_cache;
cdef bint has_breakpoint_in_frame;
# ELSE
# def trace_dispatch(self, frame, event, arg):
# ENDIF
main_debugger, filename, info, thread = self._args
main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args
# print('frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event, info.pydev_step_cmd)
try:
# print 'frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event
info.is_tracing = True
need_trace_return = False
line = frame.f_lineno
line_cache_key = (frame_cache_key, line)
if main_debugger._finish_debugging_session:
return None
if event == 'call' and main_debugger.signature_factory:
need_trace_return = send_signature_call_trace(main_debugger, frame, filename)
if event == 'return' and main_debugger.signature_factory:
send_signature_return_trace(main_debugger, frame, filename, arg)
return None
plugin_manager = main_debugger.plugin
@@ -539,19 +461,32 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if flag:
self.handle_exception(frame, event, arg)
return self.trace_dispatch
is_line = False
is_return = False
is_call = False
else:
is_line = event == 'line'
is_return = event == 'return'
is_call = event == 'call'
if not is_line and not is_return and not is_call:
# I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
return None
elif event not in ('line', 'call', 'return'):
#I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
return None
need_trace_return = False
if is_call and main_debugger.signature_factory:
need_trace_return = send_signature_call_trace(main_debugger, frame, filename)
if is_return and main_debugger.signature_factory:
send_signature_return_trace(main_debugger, frame, filename, arg)
stop_frame = info.pydev_step_stop
step_cmd = info.pydev_step_cmd
if is_exception_event:
breakpoints_for_file = None
if stop_frame and stop_frame is not frame and step_cmd is CMD_STEP_OVER and \
# CMD_STEP_OVER = 108
if stop_frame and stop_frame is not frame and step_cmd == 108 and \
arg[0] in (StopIteration, GeneratorExit) and arg[2] is None:
info.pydev_step_cmd = CMD_STEP_INTO
info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107
info.pydev_step_stop = None
else:
# If we are in single step mode and something causes us to exit the current frame, we need to make sure we break
@@ -560,29 +495,31 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
# to make a step in or step over at that location).
# Note: this is especially troublesome when we're skipping code with the
# @DontTrace comment.
if stop_frame is frame and event is 'return' and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER):
if not frame.f_code.co_flags & CO_GENERATOR:
info.pydev_step_cmd = CMD_STEP_INTO
if stop_frame is frame and is_return and step_cmd in (109, 108): # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108
if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR)
info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107
info.pydev_step_stop = None
breakpoints_for_file = main_debugger.breakpoints.get(filename)
can_skip = False
if info.pydev_state == STATE_RUN:
if info.pydev_state == 1: # STATE_RUN = 1
#we can skip if:
#- we have no stop marked
#- we should make a step return/step over and we're not in the current frame
can_skip = (step_cmd == -1 and stop_frame is None)\
or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame)
# CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108
can_skip = (step_cmd == -1 and stop_frame is None) \
or (step_cmd in (109, 108) and stop_frame is not frame)
if can_skip and plugin_manager is not None and main_debugger.has_plugin_line_breaks:
can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame)
if can_skip:
if plugin_manager is not None and main_debugger.has_plugin_line_breaks:
can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame)
if can_skip and main_debugger.show_return_values:
# trace function for showing return values after step over
if info.pydev_step_cmd == CMD_STEP_OVER and hasattr(frame, "f_back") and frame.f_back == info.pydev_step_stop:
can_skip = False
# CMD_STEP_OVER = 108
if can_skip and is_return and main_debugger.show_return_values and info.pydev_step_cmd == 108 and frame.f_back is info.pydev_step_stop:
# trace function for showing return values after step over
can_skip = False
if main_debugger.frame_eval_func and event == 'return' and info.pydev_step_cmd == -1:
frames_set = main_debugger.disable_tracing_after_exit_frames.get(get_thread_id(thread), None)
@@ -596,8 +533,8 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
# Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
# we will return nothing for the next trace
#also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
#so, that's why the additional checks are there.
# also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
# so, that's why the additional checks are there.
if not breakpoints_for_file:
if can_skip:
if has_exception_breakpoints:
@@ -609,34 +546,51 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
return None
else:
#checks the breakpoint to see if there is a context match in some function
curr_func_name = frame.f_code.co_name
# When cached, 0 means we don't have a breakpoint and 1 means we have.
if can_skip:
breakpoints_in_line_cache = frame_skips_cache.get(line_cache_key, -1)
if breakpoints_in_line_cache == 0:
return self.trace_dispatch
#global context is set with an empty name
if curr_func_name in ('?', '<module>'):
curr_func_name = ''
breakpoints_in_frame_cache = frame_skips_cache.get(frame_cache_key, -1)
if breakpoints_in_frame_cache != -1:
# Gotten from cache.
has_breakpoint_in_frame = breakpoints_in_frame_cache == 1
for breakpoint in dict_iter_values(breakpoints_for_file): #jython does not support itervalues()
#will match either global or some function
if breakpoint.func_name in ('None', curr_func_name):
break
else:
has_breakpoint_in_frame = False
# Checks the breakpoint to see if there is a context match in some function
curr_func_name = frame.f_code.co_name
else: # if we had some break, it won't get here (so, that's a context that we want to skip)
if can_skip:
if has_exception_breakpoints:
return self.trace_exception
#global context is set with an empty name
if curr_func_name in ('?', '<module>'):
curr_func_name = ''
for breakpoint in dict_iter_values(breakpoints_for_file): #jython does not support itervalues()
#will match either global or some function
if breakpoint.func_name in ('None', curr_func_name):
has_breakpoint_in_frame = True
break
# Cache the value (1 or 0 or -1 for default because of cython).
if has_breakpoint_in_frame:
frame_skips_cache[frame_cache_key] = 1
else:
frame_skips_cache[frame_cache_key] = 0
if can_skip and not has_breakpoint_in_frame:
if has_exception_breakpoints:
return self.trace_exception
else:
if need_trace_return:
return self.trace_return
else:
if need_trace_return:
return self.trace_return
else:
return None
return None
#We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame
#print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event
#print('NOT skipped', frame.f_lineno, frame.f_code.co_name, event)
try:
line = frame.f_lineno
flag = False
#return is not taken into account for breakpoint hit because we'd have a double-hit in this case
#(one for the line and the other for the return).
@@ -646,18 +600,17 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
exist_result = False
stop = False
bp_type = None
if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None \
and dict_contains(breakpoints_for_file, line):
if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and dict_contains(breakpoints_for_file, line):
breakpoint = breakpoints_for_file[line]
new_frame = frame
stop = True
if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ('line', 'return'):
if step_cmd == CMD_STEP_OVER and stop_frame is frame and (is_line or is_return):
stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
elif plugin_manager is not None and main_debugger.has_plugin_line_breaks:
result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args)
if result:
exist_result = True
(flag, breakpoint, new_frame, bp_type) = result
flag, breakpoint, new_frame, bp_type = result
if breakpoint:
#ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint
@@ -709,17 +662,16 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
info.pydev_message = str(val)
if not main_debugger.first_breakpoint_reached:
if event == 'call':
if hasattr(frame, 'f_back'):
back = frame.f_back
if back is not None:
# When we start debug session, we call execfile in pydevd run function. It produces an additional
# 'call' event for tracing and we stop on the first line of code twice.
_, back_filename, base = get_abs_path_real_path_and_base_from_frame(back)
if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \
(base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]):
stop = False
main_debugger.first_breakpoint_reached = True
if is_call:
back = frame.f_back
if back is not None:
# When we start debug session, we call execfile in pydevd run function. It produces an additional
# 'call' event for tracing and we stop on the first line of code twice.
_, back_filename, base = get_abs_path_real_path_and_base_from_frame(back)
if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \
(base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]):
stop = False
main_debugger.first_breakpoint_reached = True
else:
# if the frame is traced after breakpoint stop,
# but the file should be ignored while stepping because of filters
@@ -747,6 +699,10 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if info.pydev_state == STATE_SUSPEND:
self.do_wait_suspend(thread, frame, event, arg)
return self.trace_dispatch
else:
if not breakpoint and not is_return:
# No stop from anyone and no breakpoint found in line (cache that).
frame_skips_cache[line_cache_key] = 0
except:
traceback.print_exc()
@@ -773,7 +729,7 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
stop = False
elif step_cmd == CMD_STEP_INTO:
stop = event in ('line', 'return')
stop = is_line or is_return
if plugin_manager is not None:
result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop)
if result:
@@ -781,13 +737,13 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
elif step_cmd == CMD_STEP_INTO_MY_CODE:
if not main_debugger.not_in_scope(frame.f_code.co_filename):
stop = event == 'line'
stop = is_line
elif step_cmd == CMD_STEP_OVER:
stop = stop_frame is frame and event in ('line', 'return')
stop = stop_frame is frame and (is_line or is_return)
if frame.f_code.co_flags & CO_GENERATOR:
if event is 'return':
if is_return:
stop = False
if plugin_manager is not None:
@@ -801,7 +757,7 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
info.pydev_func_name = '.invalid.' # Must match the type in cython
info.pydev_smart_step_stop = None
if event == 'line' or event == 'exception':
if is_line or is_exception_event:
curr_func_name = frame.f_code.co_name
#global context is set with an empty name
@@ -812,12 +768,12 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
stop = True
elif step_cmd == CMD_STEP_RETURN:
stop = event == 'return' and stop_frame is frame
stop = is_return and stop_frame is frame
elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
stop = False
if event == 'line' or event == 'exception':
if is_line or is_exception_event:
#Yes, we can only act on line events (weird hum?)
#Note: This code is duplicated at pydevd.py
#Acting on exception events after debugger breaks with exception
@@ -841,19 +797,18 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
else:
stop = False
if stop and step_cmd != -1 and IS_PY3K:
# in Py3k we start script via our custom `execfile` function, and we shouldn't stop there
# while stepping when execution is finished
if event == 'return' and hasattr(frame, "f_back") and hasattr(frame.f_back, "f_code"):
back_filename = os.path.basename(frame.f_back.f_code.co_filename)
file_type = get_file_type(back_filename)
if file_type == PYDEV_FILE:
stop = False
if is_return and hasattr(frame, "f_back"):
f_code = getattr(frame.f_back, 'f_code', None)
if f_code is not None:
back_filename = os.path.basename(f_code.co_filename)
file_type = get_file_type(back_filename)
if file_type == PYDEV_FILE:
stop = False
if plugin_stop:
stopped_on_plugin = plugin_manager.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd)
elif stop:
if event == 'line':
if is_line:
self.set_suspend(thread, step_cmd)
self.do_wait_suspend(thread, frame, event, arg)
else: #return event
@@ -920,25 +875,34 @@ from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE
from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame, NORM_PATHS_AND_BASE_CONTAINER
from _pydevd_bundle.pydevd_tracing import SetTrace
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
# In Cython, PyDBAdditionalThreadInfo is bundled in the file.
from cpython.object cimport PyObject
from cpython.ref cimport Py_INCREF, Py_XDECREF
# ELSE
# from _pydevd_bundle.pydevd_additional_thread_info import PyDBAdditionalThreadInfo
# from _pydevd_bundle.pydevd_frame import PyDBFrame
# ENDIF
threadingCurrentThread = threading.currentThread
get_file_type = DONT_TRACE.get
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
# cdef dict global_cache_skips
# cdef dict global_cache_frame_skips
# ELSE
# ENDIF
# Cache where we should keep that we completely skipped entering some context.
# It needs to be invalidated when:
# - Breakpoints are changed
# It can be used when running regularly (without step over/step in/step return)
global_cache_skips = {}
global_cache_frame_skips = {}
def trace_dispatch(py_db, frame, event, arg):
#try:
t = threadingCurrentThread()
#except:
#this could give an exception (python 2.5 bug), but should not be there anymore...
#see http://mail.python.org/pipermail/python-bugs-list/2007-June/038796.html
#and related bug: http://bugs.python.org/issue1733757
#frame.f_trace = py_db.trace_dispatch
#return py_db.trace_dispatch
if getattr(t, 'pydev_do_not_trace', None):
return None
@@ -950,7 +914,7 @@ def trace_dispatch(py_db, frame, event, arg):
except:
additional_info = t.additional_info = PyDBAdditionalThreadInfo()
thread_tracer = ThreadTracer((py_db, t, additional_info))
thread_tracer = ThreadTracer((py_db, t, additional_info, global_cache_skips, global_cache_frame_skips))
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
t._tracer = thread_tracer # Hack for cython to keep it alive while the thread is alive (just the method in the SetTrace is not enough).
# ELSE
@@ -959,6 +923,18 @@ def trace_dispatch(py_db, frame, event, arg):
return thread_tracer.__call__(frame, event, arg)
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
cdef class SafeCallWrapper:
cdef method_object
def __init__(self, method_object):
self.method_object = method_object
def __call__(self, *args):
#Cannot use 'self' once inside the delegate call since we are borrowing the self reference f_trace field
#in the frame, and that reference might get destroyed by set trace on frame and parents
cdef PyObject* method_obj = <PyObject*> self.method_object
Py_INCREF(<object>method_obj)
ret = (<object>method_obj)(*args)
Py_XDECREF (method_obj)
return SafeCallWrapper(ret) if ret is not None else None
cdef class ThreadTracer:
cdef public tuple _args;
def __init__(self, tuple args):
@@ -967,7 +943,7 @@ cdef class ThreadTracer:
# class ThreadTracer:
# def __init__(self, args):
# self._args = args
# ENDIF
# ENDIF
def __call__(self, frame, event, arg):
@@ -986,10 +962,17 @@ cdef class ThreadTracer:
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
cdef str filename;
cdef str base;
cdef int pydev_step_cmd;
cdef tuple cache_key;
cdef dict cache_skips;
cdef bint is_stepping;
cdef tuple abs_path_real_path_and_base;
cdef PyDBAdditionalThreadInfo additional_info;
# ENDIF
py_db, t, additional_info = self._args
# print('ENTER: trace_dispatch', frame.f_code.co_filename, frame.f_lineno, event, frame.f_code.co_name)
py_db, t, additional_info, cache_skips, frame_skips_cache = self._args
pydev_step_cmd = additional_info.pydev_step_cmd
is_stepping = pydev_step_cmd != -1
try:
if py_db._finish_debugging_session:
@@ -1020,22 +1003,32 @@ cdef class ThreadTracer:
if py_db.asyncio_analyser is not None:
py_db.asyncio_analyser.log_event(frame)
filename = abs_path_real_path_and_base[1]
# Note: it's important that the context name is also given because we may hit something once
# in the global context and another in the local context.
cache_key = (frame.f_lineno, frame.f_code.co_name, filename)
if not is_stepping and cache_key in cache_skips:
# print('skipped: trace_dispatch (cache hit)', cache_key, frame.f_lineno, event, frame.f_code.co_name)
return None
file_type = get_file_type(abs_path_real_path_and_base[-1]) #we don't want to debug threading or anything related to pydevd
if file_type is not None:
if file_type == 1: # inlining LIB_FILE = 1
if py_db.not_in_scope(abs_path_real_path_and_base[1]):
# print('skipped: trace_dispatch (not in scope)', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
if py_db.not_in_scope(filename):
# print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
cache_skips[cache_key] = 1
return None
else:
# print('skipped: trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
# print('skipped: trace_dispatch', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
cache_skips[cache_key] = 1
return None
if additional_info.pydev_step_cmd != -1:
if py_db.is_filter_enabled and py_db.is_ignored_by_filters(abs_path_real_path_and_base[1]):
if is_stepping:
if py_db.is_filter_enabled and py_db.is_ignored_by_filters(filename):
# ignore files matching stepping filters
return None
if py_db.is_filter_libraries and py_db.not_in_scope(abs_path_real_path_and_base[1]):
if py_db.is_filter_libraries and py_db.not_in_scope(filename):
# ignore library files while stepping
return None
@@ -1043,13 +1036,17 @@ cdef class ThreadTracer:
if additional_info.is_tracing:
return None #we don't wan't to trace code invoked from pydevd_frame.trace_dispatch
# Just create PyDBFrame directly (removed support for Python versions < 2.5, which required keeping a weak
# reference to the frame).
ret = PyDBFrame((py_db, filename, additional_info, t, frame_skips_cache, (frame.f_code.co_name, frame.f_code.co_firstlineno, filename))).trace_dispatch(frame, event, arg)
if ret is None:
cache_skips[cache_key] = 1
return None
# each new frame...
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
# Note that on Cython we only support more modern idioms (no support for < Python 2.5)
return PyDBFrame((py_db, abs_path_real_path_and_base[1], additional_info, t)).trace_dispatch(frame, event, arg)
return SafeCallWrapper(ret)
# ELSE
# return additional_info.create_db_frame((py_db, abs_path_real_path_and_base[1], additional_info, t, frame)).trace_dispatch(frame, event, arg)
# return ret
# ENDIF
except SystemExit:
@@ -1,5 +1,5 @@
try:
from _pydevd_bundle.pydevd_cython import trace_dispatch, PyDBAdditionalThreadInfo
from _pydevd_bundle.pydevd_cython import trace_dispatch, PyDBAdditionalThreadInfo, global_cache_skips, global_cache_frame_skips
except ImportError:
try:
import struct
@@ -25,6 +25,7 @@ except ImportError:
check_name = '_pydevd_bundle.%s' % (mod_name,)
mod = __import__(check_name)
mod = getattr(mod, mod_name)
trace_dispatch, PyDBAdditionalThreadInfo = mod.trace_dispatch, mod.PyDBAdditionalThreadInfo
trace_dispatch, PyDBAdditionalThreadInfo, global_cache_skips, global_cache_frame_skips = \
mod.trace_dispatch, mod.PyDBAdditionalThreadInfo, mod.global_cache_skips, mod.global_cache_frame_skips
except ImportError:
raise
@@ -65,6 +65,7 @@ DONT_TRACE = {
'pydevd_additional_thread_info_regular.py': PYDEV_FILE,
'pydevd_breakpoints.py': PYDEV_FILE,
'pydevd_comm.py': PYDEV_FILE,
'pydevd_command_line_handling.py': PYDEV_FILE,
'pydevd_concurrency_logger.py': PYDEV_FILE,
'pydevd_console.py': PYDEV_FILE,
'pydevd_constants.py': PYDEV_FILE,
@@ -11,7 +11,7 @@ from _pydevd_bundle.pydevd_breakpoints import get_exception_breakpoint
from _pydevd_bundle.pydevd_comm import CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_BREAK, \
CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT, CMD_STEP_INTO_MY_CODE
from _pydevd_bundle.pydevd_constants import STATE_SUSPEND, dict_contains, get_thread_id, STATE_RUN, dict_iter_values, IS_PY3K, \
dict_keys, dict_pop, RETURN_VALUES_DICT
dict_keys, RETURN_VALUES_DICT
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
@@ -40,11 +40,16 @@ get_file_type = DONT_TRACE.get
#=======================================================================================================================
# PyDBFrame
#=======================================================================================================================
class PyDBFrame: # No longer cdef because object was dying when only a reference to trace_dispatch was kept (need to check alternatives).
# IFDEF CYTHON
# cdef class PyDBFrame:
# ELSE
class PyDBFrame:
'''This makes the tracing for a given frame, so, the trace_dispatch
is used initially when we enter into a new context ('call') and then
is reused for the entire context.
'''
# ENDIF
#Note: class (and not instance) attributes.
@@ -52,16 +57,20 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
#considers the user input (so, the actual result must be a join of both).
filename_to_lines_where_exceptions_are_ignored = {}
filename_to_stat_info = {}
should_skip = -1
# IFDEF CYTHON
# def __init__(self, args):
# self._args = args # In the cython version we don't need to pass the frame
# cdef tuple _args
# cdef int should_skip
# def __init__(self, tuple args):
# self._args = args # In the cython version we don't need to pass the frame
# self.should_skip = -1 # On cythonized version, put in instance.
# ELSE
should_skip = -1 # Default value in class (put in instance on set).
def __init__(self, args):
#args = main_debugger, filename, base, info, t, frame
#yeap, much faster than putting in self and then getting it from self later on
self._args = args[:-1] # Remove the frame (we don't want to have a reference to it).
self._args = args
# ENDIF
def set_suspend(self, *args, **kwargs):
@@ -104,7 +113,8 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
info = self._args[2]
flag = False
if info.pydev_state != STATE_SUSPEND: #and breakpoint is not None:
# STATE_SUSPEND = 2
if info.pydev_state != 2: #and breakpoint is not None:
exception, value, trace = arg
if trace is not None: #on jython trace is None on the first event
@@ -139,7 +149,7 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if main_debugger.plugin is not None:
result = main_debugger.plugin.exception_break(main_debugger, self, frame, self._args, arg)
if result:
(flag, frame) = result
flag, frame = result
except:
flag = False
@@ -266,6 +276,19 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
main_debugger = None
thread = None
def get_func_name(self, frame):
code_obj = frame.f_code
func_name = code_obj.co_name
try:
cls_name = get_clsname_for_code(code_obj, frame)
if cls_name is not None:
return "%s.%s" % (cls_name, func_name)
else:
return func_name
except:
traceback.print_exc()
return func_name
def manage_return_values(self, main_debugger, frame, event, arg):
def get_func_name(frame):
@@ -293,17 +316,17 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
# Showing return values was turned off, we should remove them from locals dict.
# The values can be in the current frame or in the back one
if RETURN_VALUES_DICT in dict_keys(frame.f_locals):
dict_pop(frame.f_locals, RETURN_VALUES_DICT)
frame.f_locals.pop(RETURN_VALUES_DICT)
if hasattr(frame, "f_back") and hasattr(frame.f_back, "f_locals"):
if RETURN_VALUES_DICT in dict_keys(frame.f_back.f_locals):
dict_pop(frame.f_back.f_locals, RETURN_VALUES_DICT)
frame.f_back.f_locals.pop(RETURN_VALUES_DICT)
main_debugger.remove_return_values_flag = False
except:
main_debugger.remove_return_values_flag = False
traceback.print_exc()
# IFDEF CYTHON
# def trace_dispatch(self, frame, str event, arg):
# cpdef trace_dispatch(self, frame, str event, arg):
# cdef str filename;
# cdef bint is_exception_event;
# cdef bint has_exception_breakpoints;
@@ -311,26 +334,31 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
# cdef PyDBAdditionalThreadInfo info;
# cdef int step_cmd;
# cdef int line;
# cdef bint is_line;
# cdef bint is_call;
# cdef bint is_return;
# cdef str curr_func_name;
# cdef bint exist_result;
# cdef dict frame_skips_cache;
# cdef tuple frame_cache_key;
# cdef tuple line_cache_key;
# cdef int breakpoints_in_line_cache;
# cdef int breakpoints_in_frame_cache;
# cdef bint has_breakpoint_in_frame;
# ELSE
def trace_dispatch(self, frame, event, arg):
# ENDIF
main_debugger, filename, info, thread = self._args
main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args
# print('frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event, info.pydev_step_cmd)
try:
# print 'frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event
info.is_tracing = True
need_trace_return = False
line = frame.f_lineno
line_cache_key = (frame_cache_key, line)
if main_debugger._finish_debugging_session:
return None
if event == 'call' and main_debugger.signature_factory:
need_trace_return = send_signature_call_trace(main_debugger, frame, filename)
if event == 'return' and main_debugger.signature_factory:
send_signature_return_trace(main_debugger, frame, filename, arg)
plugin_manager = main_debugger.plugin
is_exception_event = event == 'exception'
@@ -342,19 +370,32 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if flag:
self.handle_exception(frame, event, arg)
return self.trace_dispatch
is_line = False
is_return = False
is_call = False
else:
is_line = event == 'line'
is_return = event == 'return'
is_call = event == 'call'
if not is_line and not is_return and not is_call:
# I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
return None
elif event not in ('line', 'call', 'return'):
#I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
return None
need_trace_return = False
if is_call and main_debugger.signature_factory:
need_trace_return = send_signature_call_trace(main_debugger, frame, filename)
if is_return and main_debugger.signature_factory:
send_signature_return_trace(main_debugger, frame, filename, arg)
stop_frame = info.pydev_step_stop
step_cmd = info.pydev_step_cmd
if is_exception_event:
breakpoints_for_file = None
if stop_frame and stop_frame is not frame and step_cmd is CMD_STEP_OVER and \
# CMD_STEP_OVER = 108
if stop_frame and stop_frame is not frame and step_cmd == 108 and \
arg[0] in (StopIteration, GeneratorExit) and arg[2] is None:
info.pydev_step_cmd = CMD_STEP_INTO
info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107
info.pydev_step_stop = None
else:
# If we are in single step mode and something causes us to exit the current frame, we need to make sure we break
@@ -363,29 +404,31 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
# to make a step in or step over at that location).
# Note: this is especially troublesome when we're skipping code with the
# @DontTrace comment.
if stop_frame is frame and event is 'return' and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER):
if not frame.f_code.co_flags & CO_GENERATOR:
info.pydev_step_cmd = CMD_STEP_INTO
if stop_frame is frame and is_return and step_cmd in (109, 108): # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108
if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR)
info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107
info.pydev_step_stop = None
breakpoints_for_file = main_debugger.breakpoints.get(filename)
can_skip = False
if info.pydev_state == STATE_RUN:
if info.pydev_state == 1: # STATE_RUN = 1
#we can skip if:
#- we have no stop marked
#- we should make a step return/step over and we're not in the current frame
can_skip = (step_cmd == -1 and stop_frame is None)\
or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame)
# CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108
can_skip = (step_cmd == -1 and stop_frame is None) \
or (step_cmd in (109, 108) and stop_frame is not frame)
if can_skip and plugin_manager is not None and main_debugger.has_plugin_line_breaks:
can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame)
if can_skip:
if plugin_manager is not None and main_debugger.has_plugin_line_breaks:
can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame)
if can_skip and main_debugger.show_return_values:
# trace function for showing return values after step over
if info.pydev_step_cmd == CMD_STEP_OVER and hasattr(frame, "f_back") and frame.f_back == info.pydev_step_stop:
can_skip = False
# CMD_STEP_OVER = 108
if can_skip and is_return and main_debugger.show_return_values and info.pydev_step_cmd == 108 and frame.f_back is info.pydev_step_stop:
# trace function for showing return values after step over
can_skip = False
if main_debugger.frame_eval_func and event == 'return' and info.pydev_step_cmd == -1:
frames_set = main_debugger.disable_tracing_after_exit_frames.get(get_thread_id(thread), None)
@@ -399,8 +442,8 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
# Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
# we will return nothing for the next trace
#also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
#so, that's why the additional checks are there.
# also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
# so, that's why the additional checks are there.
if not breakpoints_for_file:
if can_skip:
if has_exception_breakpoints:
@@ -412,34 +455,51 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
return None
else:
#checks the breakpoint to see if there is a context match in some function
curr_func_name = frame.f_code.co_name
# When cached, 0 means we don't have a breakpoint and 1 means we have.
if can_skip:
breakpoints_in_line_cache = frame_skips_cache.get(line_cache_key, -1)
if breakpoints_in_line_cache == 0:
return self.trace_dispatch
#global context is set with an empty name
if curr_func_name in ('?', '<module>'):
curr_func_name = ''
breakpoints_in_frame_cache = frame_skips_cache.get(frame_cache_key, -1)
if breakpoints_in_frame_cache != -1:
# Gotten from cache.
has_breakpoint_in_frame = breakpoints_in_frame_cache == 1
for breakpoint in dict_iter_values(breakpoints_for_file): #jython does not support itervalues()
#will match either global or some function
if breakpoint.func_name in ('None', curr_func_name):
break
else:
has_breakpoint_in_frame = False
# Checks the breakpoint to see if there is a context match in some function
curr_func_name = frame.f_code.co_name
else: # if we had some break, it won't get here (so, that's a context that we want to skip)
if can_skip:
if has_exception_breakpoints:
return self.trace_exception
#global context is set with an empty name
if curr_func_name in ('?', '<module>'):
curr_func_name = ''
for breakpoint in dict_iter_values(breakpoints_for_file): #jython does not support itervalues()
#will match either global or some function
if breakpoint.func_name in ('None', curr_func_name):
has_breakpoint_in_frame = True
break
# Cache the value (1 or 0 or -1 for default because of cython).
if has_breakpoint_in_frame:
frame_skips_cache[frame_cache_key] = 1
else:
frame_skips_cache[frame_cache_key] = 0
if can_skip and not has_breakpoint_in_frame:
if has_exception_breakpoints:
return self.trace_exception
else:
if need_trace_return:
return self.trace_return
else:
if need_trace_return:
return self.trace_return
else:
return None
return None
#We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame
#print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event
#print('NOT skipped', frame.f_lineno, frame.f_code.co_name, event)
try:
line = frame.f_lineno
flag = False
#return is not taken into account for breakpoint hit because we'd have a double-hit in this case
#(one for the line and the other for the return).
@@ -449,18 +509,17 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
exist_result = False
stop = False
bp_type = None
if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None \
and dict_contains(breakpoints_for_file, line):
if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and dict_contains(breakpoints_for_file, line):
breakpoint = breakpoints_for_file[line]
new_frame = frame
stop = True
if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ('line', 'return'):
if step_cmd == CMD_STEP_OVER and stop_frame is frame and (is_line or is_return):
stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
elif plugin_manager is not None and main_debugger.has_plugin_line_breaks:
result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args)
if result:
exist_result = True
(flag, breakpoint, new_frame, bp_type) = result
flag, breakpoint, new_frame, bp_type = result
if breakpoint:
#ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint
@@ -512,17 +571,16 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
info.pydev_message = str(val)
if not main_debugger.first_breakpoint_reached:
if event == 'call':
if hasattr(frame, 'f_back'):
back = frame.f_back
if back is not None:
# When we start debug session, we call execfile in pydevd run function. It produces an additional
# 'call' event for tracing and we stop on the first line of code twice.
_, back_filename, base = get_abs_path_real_path_and_base_from_frame(back)
if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \
(base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]):
stop = False
main_debugger.first_breakpoint_reached = True
if is_call:
back = frame.f_back
if back is not None:
# When we start debug session, we call execfile in pydevd run function. It produces an additional
# 'call' event for tracing and we stop on the first line of code twice.
_, back_filename, base = get_abs_path_real_path_and_base_from_frame(back)
if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \
(base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]):
stop = False
main_debugger.first_breakpoint_reached = True
else:
# if the frame is traced after breakpoint stop,
# but the file should be ignored while stepping because of filters
@@ -550,6 +608,10 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if info.pydev_state == STATE_SUSPEND:
self.do_wait_suspend(thread, frame, event, arg)
return self.trace_dispatch
else:
if not breakpoint and not is_return:
# No stop from anyone and no breakpoint found in line (cache that).
frame_skips_cache[line_cache_key] = 0
except:
traceback.print_exc()
@@ -576,7 +638,7 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
stop = False
elif step_cmd == CMD_STEP_INTO:
stop = event in ('line', 'return')
stop = is_line or is_return
if plugin_manager is not None:
result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop)
if result:
@@ -584,13 +646,13 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
elif step_cmd == CMD_STEP_INTO_MY_CODE:
if not main_debugger.not_in_scope(frame.f_code.co_filename):
stop = event == 'line'
stop = is_line
elif step_cmd == CMD_STEP_OVER:
stop = stop_frame is frame and event in ('line', 'return')
stop = stop_frame is frame and (is_line or is_return)
if frame.f_code.co_flags & CO_GENERATOR:
if event is 'return':
if is_return:
stop = False
if plugin_manager is not None:
@@ -604,7 +666,7 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
info.pydev_func_name = '.invalid.' # Must match the type in cython
info.pydev_smart_step_stop = None
if event == 'line' or event == 'exception':
if is_line or is_exception_event:
curr_func_name = frame.f_code.co_name
#global context is set with an empty name
@@ -615,12 +677,12 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
stop = True
elif step_cmd == CMD_STEP_RETURN:
stop = event == 'return' and stop_frame is frame
stop = is_return and stop_frame is frame
elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
stop = False
if event == 'line' or event == 'exception':
if is_line or is_exception_event:
#Yes, we can only act on line events (weird hum?)
#Note: This code is duplicated at pydevd.py
#Acting on exception events after debugger breaks with exception
@@ -644,19 +706,18 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
else:
stop = False
if stop and step_cmd != -1 and IS_PY3K:
# in Py3k we start script via our custom `execfile` function, and we shouldn't stop there
# while stepping when execution is finished
if event == 'return' and hasattr(frame, "f_back") and hasattr(frame.f_back, "f_code"):
back_filename = os.path.basename(frame.f_back.f_code.co_filename)
file_type = get_file_type(back_filename)
if file_type == PYDEV_FILE:
stop = False
if is_return and hasattr(frame, "f_back"):
f_code = getattr(frame.f_back, 'f_code', None)
if f_code is not None:
back_filename = os.path.basename(f_code.co_filename)
file_type = get_file_type(back_filename)
if file_type == PYDEV_FILE:
stop = False
if plugin_stop:
stopped_on_plugin = plugin_manager.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd)
elif stop:
if event == 'line':
if is_line:
self.set_suspend(thread, step_cmd)
self.do_wait_suspend(thread, frame, event, arg)
else: #return event
@@ -18,7 +18,7 @@ from _pydevd_bundle.pydevd_comm import CMD_RUN, CMD_VERSION, CMD_LIST_THREADS, C
CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression, InternalConsoleGetCompletions, \
CMD_RUN_CUSTOM_OPERATION, InternalRunCustomOperation, CMD_IGNORE_THROWN_EXCEPTION_AT, CMD_ENABLE_DONT_TRACE, \
CMD_SHOW_RETURN_VALUES, ID_TO_MEANING, CMD_GET_DESCRIPTION, InternalGetDescription, pydevd_check_frame_for_new_breakpoint
from _pydevd_bundle.pydevd_constants import get_thread_id, IS_PY3K, DebugInfoHolder, dict_contains, dict_keys, dict_pop, \
from _pydevd_bundle.pydevd_constants import get_thread_id, IS_PY3K, DebugInfoHolder, dict_contains, dict_keys, \
STATE_RUN
@@ -538,11 +538,11 @@ def process_net_command(py_db, cmd_id, seq, text):
if exception_type == 'python':
try:
cp = py_db.break_on_uncaught_exceptions.copy()
dict_pop(cp, exception, None)
cp.pop(exception, None)
py_db.break_on_uncaught_exceptions = cp
cp = py_db.break_on_caught_exceptions.copy()
dict_pop(cp, exception, None)
cp.pop(exception, None)
py_db.break_on_caught_exceptions = cp
except:
pydev_log.debug("Error while removing exception %s"%sys.exc_info()[0])
@@ -7,24 +7,24 @@ import sys
if use_cython == 'YES':
# We must import the cython version if forcing cython
from _pydevd_bundle.pydevd_cython_wrapper import trace_dispatch as _trace_dispatch
from _pydevd_bundle.pydevd_cython_wrapper import trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips
def trace_dispatch(py_db, frame, event, arg):
return _trace_dispatch(py_db, frame, event, arg)
elif use_cython == 'NO':
# Use the regular version if not forcing cython
from _pydevd_bundle.pydevd_trace_dispatch_regular import trace_dispatch # @UnusedImport
from _pydevd_bundle.pydevd_trace_dispatch_regular import trace_dispatch, global_cache_skips, global_cache_frame_skips # @UnusedImport
elif use_cython is None:
# Regular: use fallback if not found and give message to user
try:
from _pydevd_bundle.pydevd_cython_wrapper import trace_dispatch as _trace_dispatch
from _pydevd_bundle.pydevd_cython_wrapper import trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips
def trace_dispatch(py_db, frame, event, arg):
return _trace_dispatch(py_db, frame, event, arg)
except ImportError:
from _pydevd_bundle.pydevd_additional_thread_info_regular import PyDBAdditionalThreadInfo # @UnusedImport
from _pydevd_bundle.pydevd_trace_dispatch_regular import trace_dispatch # @UnusedImport
from _pydevd_bundle.pydevd_trace_dispatch_regular import trace_dispatch, global_cache_skips, global_cache_frame_skips # @UnusedImport
from _pydevd_bundle.pydevd_constants import CYTHON_SUPPORTED
dirname = os.path.dirname(os.path.dirname(__file__))
@@ -39,5 +39,3 @@ elif use_cython is None:
else:
raise RuntimeError('Unexpected value for PYDEVD_USE_CYTHON: %s (accepted: YES, NO)' % (use_cython,))
@@ -7,25 +7,34 @@ from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE
from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame, NORM_PATHS_AND_BASE_CONTAINER
from _pydevd_bundle.pydevd_tracing import SetTrace
# IFDEF CYTHON
# # In Cython, PyDBAdditionalThreadInfo is bundled in the file.
# from cpython.object cimport PyObject
# from cpython.ref cimport Py_INCREF, Py_XDECREF
# ELSE
from _pydevd_bundle.pydevd_additional_thread_info import PyDBAdditionalThreadInfo
from _pydevd_bundle.pydevd_frame import PyDBFrame
# ENDIF
threadingCurrentThread = threading.currentThread
get_file_type = DONT_TRACE.get
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
# cdef dict global_cache_skips
# cdef dict global_cache_frame_skips
# ELSE
# ENDIF
# Cache where we should keep that we completely skipped entering some context.
# It needs to be invalidated when:
# - Breakpoints are changed
# It can be used when running regularly (without step over/step in/step return)
global_cache_skips = {}
global_cache_frame_skips = {}
def trace_dispatch(py_db, frame, event, arg):
#try:
t = threadingCurrentThread()
#except:
#this could give an exception (python 2.5 bug), but should not be there anymore...
#see http://mail.python.org/pipermail/python-bugs-list/2007-June/038796.html
#and related bug: http://bugs.python.org/issue1733757
#frame.f_trace = py_db.trace_dispatch
#return py_db.trace_dispatch
if getattr(t, 'pydev_do_not_trace', None):
return None
@@ -37,7 +46,7 @@ def trace_dispatch(py_db, frame, event, arg):
except:
additional_info = t.additional_info = PyDBAdditionalThreadInfo()
thread_tracer = ThreadTracer((py_db, t, additional_info))
thread_tracer = ThreadTracer((py_db, t, additional_info, global_cache_skips, global_cache_frame_skips))
# IFDEF CYTHON
# t._tracer = thread_tracer # Hack for cython to keep it alive while the thread is alive (just the method in the SetTrace is not enough).
# ELSE
@@ -46,6 +55,18 @@ def trace_dispatch(py_db, frame, event, arg):
return thread_tracer.__call__(frame, event, arg)
# IFDEF CYTHON
# cdef class SafeCallWrapper:
# cdef method_object
# def __init__(self, method_object):
# self.method_object = method_object
# def __call__(self, *args):
# #Cannot use 'self' once inside the delegate call since we are borrowing the self reference f_trace field
# #in the frame, and that reference might get destroyed by set trace on frame and parents
# cdef PyObject* method_obj = <PyObject*> self.method_object
# Py_INCREF(<object>method_obj)
# ret = (<object>method_obj)(*args)
# Py_XDECREF (method_obj)
# return SafeCallWrapper(ret) if ret is not None else None
# cdef class ThreadTracer:
# cdef public tuple _args;
# def __init__(self, tuple args):
@@ -54,7 +75,7 @@ def trace_dispatch(py_db, frame, event, arg):
class ThreadTracer:
def __init__(self, args):
self._args = args
# ENDIF
# ENDIF
def __call__(self, frame, event, arg):
@@ -73,10 +94,17 @@ class ThreadTracer:
# IFDEF CYTHON
# cdef str filename;
# cdef str base;
# cdef int pydev_step_cmd;
# cdef tuple cache_key;
# cdef dict cache_skips;
# cdef bint is_stepping;
# cdef tuple abs_path_real_path_and_base;
# cdef PyDBAdditionalThreadInfo additional_info;
# ENDIF
py_db, t, additional_info = self._args
# print('ENTER: trace_dispatch', frame.f_code.co_filename, frame.f_lineno, event, frame.f_code.co_name)
py_db, t, additional_info, cache_skips, frame_skips_cache = self._args
pydev_step_cmd = additional_info.pydev_step_cmd
is_stepping = pydev_step_cmd != -1
try:
if py_db._finish_debugging_session:
@@ -107,22 +135,32 @@ class ThreadTracer:
if py_db.asyncio_analyser is not None:
py_db.asyncio_analyser.log_event(frame)
filename = abs_path_real_path_and_base[1]
# Note: it's important that the context name is also given because we may hit something once
# in the global context and another in the local context.
cache_key = (frame.f_lineno, frame.f_code.co_name, filename)
if not is_stepping and cache_key in cache_skips:
# print('skipped: trace_dispatch (cache hit)', cache_key, frame.f_lineno, event, frame.f_code.co_name)
return None
file_type = get_file_type(abs_path_real_path_and_base[-1]) #we don't want to debug threading or anything related to pydevd
if file_type is not None:
if file_type == 1: # inlining LIB_FILE = 1
if py_db.not_in_scope(abs_path_real_path_and_base[1]):
# print('skipped: trace_dispatch (not in scope)', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
if py_db.not_in_scope(filename):
# print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
cache_skips[cache_key] = 1
return None
else:
# print('skipped: trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
# print('skipped: trace_dispatch', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
cache_skips[cache_key] = 1
return None
if additional_info.pydev_step_cmd != -1:
if py_db.is_filter_enabled and py_db.is_ignored_by_filters(abs_path_real_path_and_base[1]):
if is_stepping:
if py_db.is_filter_enabled and py_db.is_ignored_by_filters(filename):
# ignore files matching stepping filters
return None
if py_db.is_filter_libraries and py_db.not_in_scope(abs_path_real_path_and_base[1]):
if py_db.is_filter_libraries and py_db.not_in_scope(filename):
# ignore library files while stepping
return None
@@ -130,13 +168,17 @@ class ThreadTracer:
if additional_info.is_tracing:
return None #we don't wan't to trace code invoked from pydevd_frame.trace_dispatch
# Just create PyDBFrame directly (removed support for Python versions < 2.5, which required keeping a weak
# reference to the frame).
ret = PyDBFrame((py_db, filename, additional_info, t, frame_skips_cache, (frame.f_code.co_name, frame.f_code.co_firstlineno, filename))).trace_dispatch(frame, event, arg)
if ret is None:
cache_skips[cache_key] = 1
return None
# each new frame...
# IFDEF CYTHON
# # Note that on Cython we only support more modern idioms (no support for < Python 2.5)
# return PyDBFrame((py_db, abs_path_real_path_and_base[1], additional_info, t)).trace_dispatch(frame, event, arg)
# return SafeCallWrapper(ret)
# ELSE
return additional_info.create_db_frame((py_db, abs_path_real_path_and_base[1], additional_info, t, frame)).trace_dispatch(frame, event, arg)
return ret
# ENDIF
except SystemExit:
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@ import dis
from _pydev_imps._pydev_saved_modules import threading
from _pydevd_bundle.pydevd_additional_thread_info import PyDBAdditionalThreadInfo
from _pydevd_bundle.pydevd_comm import get_global_debugger, CMD_THREAD_SUSPEND
from _pydevd_bundle.pydevd_constants import STATE_SUSPEND
from _pydevd_bundle.pydevd_constants import STATE_RUN, STATE_SUSPEND
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE
from _pydevd_frame_eval.pydevd_frame_tracing import pydev_trace_code_wrapper, update_globals_dict
from _pydevd_frame_eval.pydevd_modify_bytecode import insert_code
@@ -93,7 +93,8 @@ cdef PyObject* get_bytecode_while_frame_eval(PyFrameObject *frame_obj, int exc):
return _PyEval_EvalFrameDefault(frame_obj, exc)
main_debugger = get_global_debugger()
if additional_info.pydev_state == STATE_SUSPEND and t.stop_reason == CMD_THREAD_SUSPEND:
if (additional_info.pydev_state == STATE_SUSPEND and t.stop_reason == CMD_THREAD_SUSPEND) or \
(additional_info.pydev_state == STATE_RUN and main_debugger.disable_tracing_after_exit_frames):
main_debugger.process_internal_commands()
was_break = False
+45 -98
View File
@@ -10,7 +10,7 @@ import os
import sys
import traceback
from _pydevd_bundle.pydevd_constants import IS_JYTH_LESS25, IS_PY3K, IS_PY34_OLDER, get_thread_id, dict_keys, dict_pop, dict_contains, \
from _pydevd_bundle.pydevd_constants import IS_JYTH_LESS25, IS_PY3K, IS_PY34_OLDER, get_thread_id, dict_keys, dict_contains, \
dict_iter_items, DebugInfoHolder, PYTHON_SUSPEND, STATE_SUSPEND, STATE_RUN, get_frame, xrange, \
clear_cached_thread_id, INTERACTIVE_MODE_AVAILABLE
from _pydev_bundle import fix_getpass
@@ -42,7 +42,7 @@ from pydevd_concurrency_analyser.pydevd_concurrency_logger import ThreadingLogge
from pydevd_concurrency_analyser.pydevd_thread_wrappers import wrap_threads
__version_info__ = (0, 0, 5)
__version_info__ = (1, 0, 0)
__version_info_str__ = []
for v in __version_info__:
__version_info_str__.append(str(v))
@@ -247,6 +247,9 @@ class PyDB:
self.mpl_hooks_in_debug_console = False
self.mpl_modules_for_patching = {}
# this dict is used for frame evaluation, it holds thread_id -> set of frames
self.disable_tracing_after_exit_frames = {}
self._filename_to_not_in_scope = {}
self.first_breakpoint_reached = False
self.is_filter_enabled = pydevd_utils.is_filter_enabled()
@@ -254,9 +257,6 @@ class PyDB:
self.show_return_values = False
self.remove_return_values_flag = False
# this dict is used for frame evaluation, it holds thread_id -> set of frames
self.disable_tracing_after_exit_frames = {}
def get_plugin_lazy_init(self):
if self.plugin is None and SUPPORT_PLUGINS:
self.plugin = PluginManager(self)
@@ -379,7 +379,7 @@ class PyDB:
# import hook and patches for matplotlib support in debug console
from _pydev_bundle.pydev_import_hook import import_hook_manager
for module in dict_keys(self.mpl_modules_for_patching):
import_hook_manager.add_module_name(module, dict_pop(self.mpl_modules_for_patching, module))
import_hook_manager.add_module_name(module, self.mpl_modules_for_patching.pop(module))
def init_matplotlib_support(self):
# prepare debugger for integration with matplotlib GUI event loop
@@ -408,7 +408,7 @@ class PyDB:
if len(self.mpl_modules_for_patching) > 0:
for module in dict_keys(self.mpl_modules_for_patching):
if module in sys.modules:
activate_function = dict_pop(self.mpl_modules_for_patching, module)
activate_function = self.mpl_modules_for_patching.pop(module)
activate_function()
self.mpl_in_use = True
@@ -586,11 +586,14 @@ class PyDB:
def consolidate_breakpoints(self, file, id_to_breakpoint, breakpoints):
from _pydevd_bundle.pydevd_trace_dispatch import global_cache_skips, global_cache_frame_skips
break_dict = {}
for breakpoint_id, pybreakpoint in dict_iter_items(id_to_breakpoint):
break_dict[pybreakpoint.line] = pybreakpoint
breakpoints[file] = break_dict
global_cache_skips.clear()
global_cache_frame_skips.clear()
def add_break_on_exception(
self,
@@ -931,8 +934,10 @@ class PyDB:
return meth(mod_name)
return None
def run(self, file, globals=None, locals=None, module=False, set_trace=True):
if module:
def run(self, file, globals=None, locals=None, is_module=False, set_trace=True):
module_name = None
if is_module:
module_name = file
filename = self.get_fullname(file)
if filename is None:
sys.stderr.write("No module named %s\n" % file)
@@ -971,11 +976,12 @@ class PyDB:
# print >> sys.stderr, 'Deleting: ', sys.path[0]
del sys.path[0]
# now, the local directory has to be added to the pythonpath
# sys.path.insert(0, os.getcwd())
# Changed: it's not the local directory, but the directory of the file launched
# The file being run ust be in the pythonpath (even if it was not before)
sys.path.insert(0, os.path.split(file)[0])
if not is_module:
# now, the local directory has to be added to the pythonpath
# sys.path.insert(0, os.getcwd())
# Changed: it's not the local directory, but the directory of the file launched
# The file being run must be in the pythonpath (even if it was not before)
sys.path.insert(0, os.path.split(file)[0])
while not self.ready_to_run:
time.sleep(0.1) # busy wait until we receive run command
@@ -1005,7 +1011,16 @@ class PyDB:
sys.stderr.write("Matplotlib support in debugger failed\n")
traceback.print_exc()
pydev_imports.execfile(file, globals, locals) # execute the script
if not is_module:
pydev_imports.execfile(file, globals, locals) # execute the script
else:
# Run with the -m switch
import runpy
if hasattr(runpy, '_run_module_as_main'):
# Newer versions of Python actually use this when the -m switch is used.
runpy._run_module_as_main(module_name, alter_argv=False)
else:
runpy.run_module(module_name)
return globals
def exiting(self):
@@ -1045,91 +1060,14 @@ def set_debug(setup):
setup['DEBUG_TRACE_LEVEL'] = 3
def enable_qt_support():
def enable_qt_support(qt_support_mode):
from _pydev_bundle import pydev_monkey_qt
pydev_monkey_qt.patch_qt()
pydev_monkey_qt.patch_qt(qt_support_mode)
def process_command_line(argv):
""" parses the arguments.
removes our arguments from the command line """
setup = {}
setup['client'] = None
setup['server'] = False
setup['port'] = 0
setup['file'] = ''
setup['multiproc'] = False #Used by PyCharm (reuses connection: ssh tunneling)
setup['multiprocess'] = False # Used by PyDev (creates new connection to ide)
setup['save-signatures'] = False
setup['save-threading'] = False
setup['save-asyncio'] = False
setup['qt-support'] = False
setup['print-in-debugger-startup'] = False
setup['cmd-line'] = False
setup['module'] = False
i = 0
del argv[0]
while (i < len(argv)):
if argv[i] == '--port':
del argv[i]
setup['port'] = int(argv[i])
del argv[i]
elif argv[i] == '--vm_type':
del argv[i]
setup['vm_type'] = argv[i]
del argv[i]
elif argv[i] == '--client':
del argv[i]
setup['client'] = argv[i]
del argv[i]
elif argv[i] == '--server':
del argv[i]
setup['server'] = True
elif argv[i] == '--file':
del argv[i]
setup['file'] = argv[i]
i = len(argv) # pop out, file is our last argument
elif argv[i] == '--DEBUG_RECORD_SOCKET_READS':
del argv[i]
setup['DEBUG_RECORD_SOCKET_READS'] = True
elif argv[i] == '--DEBUG':
del argv[i]
set_debug(setup)
elif argv[i] == '--multiproc':
del argv[i]
setup['multiproc'] = True
elif argv[i] == '--multiprocess':
del argv[i]
setup['multiprocess'] = True
elif argv[i] == '--save-signatures':
del argv[i]
setup['save-signatures'] = True
elif argv[i] == '--save-threading':
del argv[i]
setup['save-threading'] = True
elif argv[i] == '--save-asyncio':
del argv[i]
setup['save-asyncio'] = True
elif argv[i] == '--qt-support':
del argv[i]
setup['qt-support'] = True
elif argv[i] == '--print-in-debugger-startup':
del argv[i]
setup['print-in-debugger-startup'] = True
elif (argv[i] == '--cmd-line'):
del argv[i]
setup['cmd-line'] = True
elif (argv[i] == '--module'):
del argv[i]
setup['module'] = True
else:
raise ValueError("unexpected option " + argv[i])
return setup
def usage(doExit=0):
sys.stdout.write('Usage:\n')
sys.stdout.write('pydevd.py --port=N [(--client hostname) | --server] --file executable [file_options]\n')
sys.stdout.write('pydevd.py --port N [(--client hostname) | --server] --file executable [file_options]\n')
if doExit:
sys.exit(0)
@@ -1237,6 +1175,15 @@ def _locked_settrace(
if not connected :
pydevd_vm_type.setup_type()
if SetupHolder.setup is None:
setup = {
'client': host, # dispatch expects client to be set to the host address when server is False
'server': False,
'port': int(port),
'multiprocess': patch_multiprocessing,
}
SetupHolder.setup = setup
debugger = PyDB()
debugger.connect(host, port) # Note: connect can raise error.
@@ -1437,7 +1384,7 @@ def apply_debugger_options(setup_options):
:type setup_options: dict[str, bool]
"""
default_options = {'save-signatures': False, 'qt-support': False}
default_options = {'save-signatures': False, 'qt-support': ''}
default_options.update(setup_options)
setup_options = default_options
@@ -1451,7 +1398,7 @@ def apply_debugger_options(setup_options):
debugger.signature_factory = SignatureFactory()
if setup_options['qt-support']:
enable_qt_support()
enable_qt_support(setup_options['qt-support'])
def patch_stdin(debugger):
@@ -1467,7 +1414,7 @@ if __name__ == '__main__':
# parse the command line. --file is our last argument that is required
try:
sys.original_argv = sys.argv[:]
from _pydevd_bundle.pydevd_command_line_handling import process_command_line
setup = process_command_line(sys.argv)
SetupHolder.setup = setup
except ValueError:
@@ -280,8 +280,8 @@ def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, sh
if is_64 != is_python_64bit():
raise RuntimeError("The architecture of the Python used to connect doesn't match the architecture of the target.\n"
"Target 64 bits: %s\n"
"Current Python 64 bits: %s" % (is_64, is_python_64bit()))
"Target 64 bits: %s\n"
"Current Python 64 bits: %s" % (is_64, is_python_64bit()))
print('Connecting to %s bits target' % (bits,))
assert resolve_label(process, compat.b('PyGILState_Ensure'))
@@ -366,16 +366,16 @@ def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, sh
# Uncomment to see the disassembled version of what we just did...
# with open('f.asm', 'wb') as stream:
# stream.write(code)
#
# exe = r'x:\nasm\nasm-2.07-win32\nasm-2.07\ndisasm.exe'
# if is_64:
# arch = '64'
# else:
# arch = '32'
#
# subprocess.call((exe + ' -b %s f.asm' % arch).split())
# with open('f.asm', 'wb') as stream:
# stream.write(code)
#
# exe = r'x:\nasm\nasm-2.07-win32\nasm-2.07\ndisasm.exe'
# if is_64:
# arch = '64'
# else:
# arch = '32'
#
# subprocess.call((exe + ' -b %s f.asm' % arch).split())
print('Injecting code to target process')
thread, _thread_address = process.inject_code(code, 0)
@@ -414,7 +414,7 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show
print('Attaching with arch: %s'% (arch,))
target_dll = os.path.join(filedir, 'attach_linux_%s.so' % suffix)
target_dll = os.path.normpath(target_dll)
target_dll = os.path.abspath(os.path.normpath(target_dll))
if not os.path.exists(target_dll):
raise RuntimeError('Could not find dll file to inject: %s' % target_dll)
@@ -428,11 +428,11 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show
'--nw', # no gui interface
'--nh', # no ~/.gdbinit
'--nx', # no .gdbinit
# '--quiet', # no version number on startup
# '--quiet', # no version number on startup
'--pid',
str(pid),
'--batch',
# '--batch-silent',
# '--batch-silent',
]
cmd.extend(["--eval-command='set scheduler-locking off'"]) # If on we'll deadlock.
@@ -449,7 +449,7 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show
if connect_debugger_tracing:
cmd.extend([
"--command='%s'" % (gdb_threads_settrace_file,),
])
])
#print ' '.join(cmd)
@@ -527,8 +527,8 @@ def run_python_code_mac(pid, python_code, connect_debugger_tracing=False, show_d
"-o 'process attach --pid %d'"%pid,
"-o 'command script import \"%s\"'" % (lldb_prepare_file,),
"-o 'load_lib_and_attach \"%s\" %s \"%s\" %s'" % (target_dll,
is_debug, python_code, show_debug_info),
])
is_debug, python_code, show_debug_info),
])
if connect_debugger_tracing:
@@ -556,7 +556,7 @@ def run_python_code_mac(pid, python_code, connect_debugger_tracing=False, show_d
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
)
print('Running lldb in target process.')
out, err = p.communicate()
print('stdout: %s' % (out,))
@@ -352,7 +352,8 @@ def has_line_breaks(plugin):
def cmd_step_into(plugin, main_debugger, frame, event, args, stop_info, stop):
main_debugger, filename, info, thread = args
info = args[2]
thread = args[3]
plugin_stop = False
if _is_django_suspended(thread):
stop_info['django_stop'] = event == 'call' and _is_django_render_call(frame)
@@ -364,7 +365,8 @@ def cmd_step_into(plugin, main_debugger, frame, event, args, stop_info, stop):
def cmd_step_over(plugin, main_debugger, frame, event, args, stop_info, stop):
main_debugger, filename, info, thread = args
info = args[2]
thread = args[3]
plugin_stop = False
if _is_django_suspended(thread):
stop_info['django_stop'] = event == 'call' and _is_django_render_call(frame)
@@ -382,7 +384,8 @@ def cmd_step_over(plugin, main_debugger, frame, event, args, stop_info, stop):
def stop(plugin, main_debugger, frame, event, args, stop_info, arg, step_cmd):
main_debugger, filename, info, thread = args
main_debugger = args[0]
thread = args[3]
if dict_contains(stop_info, 'django_stop') and stop_info['django_stop']:
frame = suspend_django(main_debugger, thread, frame, step_cmd)
if frame:
@@ -392,7 +395,9 @@ def stop(plugin, main_debugger, frame, event, args, stop_info, arg, step_cmd):
def get_breakpoint(plugin, main_debugger, pydb_frame, frame, event, args):
main_debugger, filename, info, thread = args
main_debugger = args[0]
filename = args[1]
info = args[2]
flag = False
django_breakpoint = None
new_frame = None
@@ -421,10 +426,11 @@ def suspend(plugin, main_debugger, thread, frame, bp_type):
return None
def exception_break(plugin, main_debugger, pydb_frame, frame, args, arg):
main_debugger, filename, info, thread = args
main_debugger = args[0]
thread = args[3]
exception, value, trace = arg
if main_debugger.django_exception_break and \
get_exception_name(exception) in ['VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] and \
get_exception_name(exception) in ['VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] and \
just_raised(trace) and _is_django_exception_break_context(frame):
render_frame = _find_django_render_frame(frame)
if render_frame:
@@ -238,7 +238,8 @@ def can_not_skip(plugin, pydb, pydb_frame, frame):
def cmd_step_into(plugin, pydb, frame, event, args, stop_info, stop):
pydb, filename, info, thread = args
info = args[2]
thread = args[3]
plugin_stop = False
stop_info['jinja2_stop'] = False
if _is_jinja2_suspended(thread):
@@ -271,7 +272,8 @@ def cmd_step_into(plugin, pydb, frame, event, args, stop_info, stop):
def cmd_step_over(plugin, pydb, frame, event, args, stop_info, stop):
pydb, filename, info, thread = args
info = args[2]
thread = args[3]
plugin_stop = False
stop_info['jinja2_stop'] = False
if _is_jinja2_suspended(thread):
@@ -311,7 +313,8 @@ def cmd_step_over(plugin, pydb, frame, event, args, stop_info, stop):
def stop(plugin, pydb, frame, event, args, stop_info, arg, step_cmd):
pydb, filename, info, thread = args
pydb = args[0]
thread = args[3]
if dict_contains(stop_info, 'jinja2_stop') and stop_info['jinja2_stop']:
frame = _suspend_jinja2(pydb, thread, frame, step_cmd)
if frame:
@@ -321,7 +324,9 @@ def stop(plugin, pydb, frame, event, args, stop_info, arg, step_cmd):
def get_breakpoint(plugin, pydb, pydb_frame, frame, event, args):
pydb, filename, info, thread = args
pydb= args[0]
filename = args[1]
info = args[2]
new_frame = None
jinja2_breakpoint = None
flag = False
@@ -357,7 +362,8 @@ def suspend(plugin, pydb, thread, frame, bp_type):
def exception_break(plugin, pydb, pydb_frame, frame, args, arg):
pydb, filename, info, thread = args
pydb = args[0]
thread = args[3]
exception, value, trace = arg
if pydb.jinja2_exception_break:
exception_type = dict_keys(pydb.jinja2_exception_break)[0]
+1
View File
@@ -104,6 +104,7 @@ args = dict(
'pydevconsole',
'pydevd_file_utils',
'pydevd',
'pydevd_tracing',
# 'runfiles', -- Not needed for debugger
# 'setup_cython', -- Should not be included as a module
# 'setup', -- Should not be included as a module
@@ -0,0 +1,3 @@
from . import _debugger_case_m_switch_2
print(_debugger_case_m_switch_2.ClassToBeImported)
print('TEST SUCEEDED!')
@@ -0,0 +1,2 @@
class ClassToBeImported(object):
pass
@@ -0,0 +1,37 @@
try:
from PySide import QtCore
except:
from PyQt4 import QtCore
class TestObject(QtCore.QObject):
"""
Test class providing some non-argument signal
"""
try:
testSignal = QtCore.Signal() # @UndefinedVariable
except:
testSignal = QtCore.pyqtSignal() # @UndefinedVariable
class TestThread(QtCore.QThread):
def run(self):
QtCore.QThread.sleep(2)
print('Done sleeping')
def on_start():
print('On start called')
app = QtCore.QCoreApplication([])
some_thread = TestThread()
some_object = TestObject()
# connect QThread.started to the signal
some_thread.started.connect(some_object.testSignal)
some_object.testSignal.connect(on_start)
some_thread.finished.connect(app.quit)
some_thread.start()
app.exec_()
print('TEST SUCEEDED!')
@@ -0,0 +1,14 @@
if __name__ == '__main__':
import os
import sys
root_dirname = os.path.dirname(os.path.dirname(__file__))
if root_dirname not in sys.path:
sys.path.append(root_dirname)
import pydevd
print('before pydevd.settrace')
pydevd.settrace(port=8787)
print('after pydevd.settrace')
print('TEST SUCEEDED!')
@@ -0,0 +1,25 @@
if __name__ == '__main__':
import subprocess
import sys
import os
import _debugger_case_remote_2
root_dirname = os.path.dirname(os.path.dirname(__file__))
if root_dirname not in sys.path:
sys.path.append(root_dirname)
import pydevd
print('before pydevd.settrace')
sys.stdout.flush()
pydevd.settrace(port=8787, patch_multiprocessing=True)
print('after pydevd.settrace')
sys.stdout.flush()
f = _debugger_case_remote_2.__file__
if f.endswith('.pyc'):
f = f[:-1]
print('before call')
sys.stdout.flush()
subprocess.check_call([sys.executable, '-u', f])
print('after call')
sys.stdout.flush()
@@ -0,0 +1,11 @@
if __name__ == '__main__':
print('Run as main: %s' % (__file__,))
import sys
sys.stdout.flush()
import pydevd
# Just check that we're already connected
assert pydevd.GetGlobalDebugger() is not None
print('finish')
sys.stdout.flush()
print('TEST SUCEEDED!')
sys.stdout.flush()
@@ -0,0 +1,26 @@
import time
start_time = time.time()
try:
xrange # @UndefinedVariable
except NameError:
xrange = range
from itertools import groupby
from random import randrange
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
# create an array of random strings of 40 characters each
l = sorted([''.join([letters[randrange(0, 26)] for _ in range(40)]) for _ in xrange(10000)])
# group by the first two characters
g = {k: list(v) for k, v in groupby(l, lambda x: x[:2])}
print(len(g.get('AA')))
if False:
pass # Breakpoint here
print('TotalTime>>%s<<' % (time.time()-start_time,))
print('TEST SUCEEDED')
@@ -0,0 +1,20 @@
import time
start_time = time.time()
try:
xrange # @UndefinedVariable
except NameError:
xrange = range
# do some busy work in parallel
print("Started main task")
x = 0
for i in xrange(1000000):
x += 1
print("Completed main task")
if False:
pass # Breakpoint here
print('TotalTime>>%s<<' % (time.time()-start_time,))
print('TEST SUCEEDED')
@@ -103,12 +103,13 @@ class ReaderThread(threading.Thread):
self.sock = sock
self.last_received = ''
self.all_received = []
self._kill = False
def run(self):
last_printed = None
try:
buf = ''
while True:
while not self._kill:
l = self.sock.recv(1024)
if IS_PY3K:
l = l.decode('utf-8')
@@ -127,9 +128,13 @@ class ReaderThread(threading.Thread):
print('Test Reader Thread Received %s' % last_printed)
except:
pass # ok, finished it
finally:
del self.all_received[:]
def do_kill(self):
self.sock.close()
self._kill = True
if hasattr(self, 'sock'):
self.sock.close()
class DebuggerRunner(object):
@@ -145,7 +150,7 @@ class DebuggerRunner(object):
port = int(writer_thread.port)
localhost = pydev_localhost.get_localhost()
return args + [
ret = args + [
writer_thread.get_pydevd_file(),
'--DEBUG_RECORD_SOCKET_READS',
'--qt-support',
@@ -153,25 +158,40 @@ class DebuggerRunner(object):
localhost,
'--port',
str(port),
'--file',
] + writer_thread.get_command_line_args()
return args
]
if writer_thread.IS_MODULE:
ret += ['--module']
ret = ret + ['--file'] + writer_thread.get_command_line_args()
return ret
def check_case(self, writer_thread_class):
writer_thread = writer_thread_class()
writer_thread.start()
while not hasattr(writer_thread, 'port'):
time.sleep(.01)
self.writer_thread = writer_thread
try:
writer_thread.start()
for _i in xrange(40000):
if hasattr(writer_thread, 'port'):
break
time.sleep(.01)
self.writer_thread = writer_thread
args = self.get_command_line()
args = self.get_command_line()
args = self.add_command_line_args(args)
args = self.add_command_line_args(args)
if SHOW_OTHER_DEBUG_INFO:
print('executing', ' '.join(args))
if SHOW_OTHER_DEBUG_INFO:
print('executing', ' '.join(args))
return self.run_process(args, writer_thread)
ret = self.run_process(args, writer_thread)
finally:
writer_thread.do_kill()
writer_thread.log = []
stdout = ret['stdout']
stderr = ret['stderr']
writer_thread.additional_output_checks(''.join(stdout), ''.join(stderr))
return ret
def create_process(self, args, writer_thread):
process = subprocess.Popen(
@@ -187,78 +207,88 @@ class DebuggerRunner(object):
process = self.create_process(args, writer_thread)
stdout = []
stderr = []
finish = [False]
def read(stream, buffer):
for line in stream.readlines():
if IS_PY3K:
line = line.decode('utf-8')
try:
def read(stream, buffer):
for line in stream.readlines():
if finish[0]:
return
if IS_PY3K:
line = line.decode('utf-8')
if SHOW_STDOUT:
sys.stdout.write('stdout: %s' % (line,))
buffer.append(line)
if SHOW_STDOUT:
sys.stdout.write('stdout: %s' % (line,))
buffer.append(line)
start_new_thread(read, (process.stdout, stdout))
start_new_thread(read, (process.stdout, stdout))
if SHOW_OTHER_DEBUG_INFO:
print('Both processes started')
if SHOW_OTHER_DEBUG_INFO:
print('Both processes started')
# polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to
# finish successfully).
check = 0
while True:
if process.poll() is not None:
break
else:
if writer_thread is not None:
if not writer_thread.isAlive():
if writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
process.kill()
continue
# polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to
# finish successfully).
check = 0
while True:
if process.poll() is not None:
break
else:
if writer_thread is not None:
if not writer_thread.isAlive():
if writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
process.kill()
continue
check += 1
if check == 20:
print('Warning: writer thread exited and process still did not.')
if check == 100:
process.kill()
time.sleep(.2)
self.fail_with_message(
"The other process should've exited but still didn't (timeout for process to exit).",
stdout, stderr, writer_thread
)
time.sleep(.2)
if writer_thread is not None:
if not writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
poll = process.poll()
if poll < 0:
self.fail_with_message(
"The other process exited with error code: " + str(poll), stdout, stderr, writer_thread)
if stdout is None:
self.fail_with_message(
"The other process may still be running -- and didn't give any output.", stdout, stderr, writer_thread)
check = 0
while 'TEST SUCEEDED' not in ''.join(stdout):
check += 1
if check == 20:
print('Warning: writer thread exited and process still did not.')
if check == 100:
process.kill()
time.sleep(.2)
self.fail_with_message(
"The other process should've exited but still didn't (timeout for process to exit).",
stdout, stderr, writer_thread
)
time.sleep(.2)
if check == 50:
self.fail_with_message("TEST SUCEEDED not found in stdout.", stdout, stderr, writer_thread)
time.sleep(.1)
for _i in xrange(100):
if not writer_thread.finished_ok:
time.sleep(.1)
if writer_thread is not None:
if not writer_thread.FORCE_KILL_PROCESS_WHEN_FINISHED_OK:
poll = process.poll()
if poll < 0:
self.fail_with_message(
"The other process exited with error code: " + str(poll), stdout, stderr, writer_thread)
if stdout is None:
self.fail_with_message(
"The other process may still be running -- and didn't give any output.", stdout, stderr, writer_thread)
if 'TEST SUCEEDED' not in ''.join(stdout):
self.fail_with_message("TEST SUCEEDED not found in stdout.", stdout, stderr, writer_thread)
for i in xrange(100):
if not writer_thread.finished_ok:
time.sleep(.1)
if not writer_thread.finished_ok:
self.fail_with_message(
"The thread that was doing the tests didn't finish successfully.", stdout, stderr, writer_thread)
self.fail_with_message(
"The thread that was doing the tests didn't finish successfully.", stdout, stderr, writer_thread)
finally:
finish[0] = True
return {'stdout':stdout, 'stderr':stderr}
def fail_with_message(self, msg, stdout, stderr, writerThread):
raise AssertionError(msg+
"\nStdout: \n"+'\n'.join(stdout)+
"\nStderr:"+'\n'.join(stderr)+
"\nLog:\n"+'\n'.join(getattr(writerThread, 'log', [])))
"\n\n===========================\nStdout: \n"+''.join(stdout)+
"\n\n===========================\nStderr:"+''.join(stderr)+
"\n\n===========================\nLog:\n"+'\n'.join(getattr(writerThread, 'log', [])))
@@ -268,6 +298,7 @@ class DebuggerRunner(object):
class AbstractWriterThread(threading.Thread):
FORCE_KILL_PROCESS_WHEN_FINISHED_OK = False
IS_MODULE = False
def __init__(self):
threading.Thread.__init__(self)
@@ -276,6 +307,9 @@ class AbstractWriterThread(threading.Thread):
self._next_breakpoint_id = 0
self.log = []
def additional_output_checks(self, stdout, stderr):
pass
def get_environ(self):
return None
@@ -291,12 +325,17 @@ class AbstractWriterThread(threading.Thread):
return [self.TEST_FILE]
def do_kill(self):
if hasattr(self, 'server_socket'):
self.server_socket.close()
if hasattr(self, 'reader_thread'):
# if it's not created, it's not there...
self.reader_thread.do_kill()
self.sock.close()
if hasattr(self, 'sock'):
self.sock.close()
def write(self, s):
self.log.append('write: %s' % (s,))
last = self.reader_thread.last_received
if SHOW_WRITES_AND_READS:
@@ -313,16 +352,17 @@ class AbstractWriterThread(threading.Thread):
time.sleep(0.1)
def start_socket(self):
def start_socket(self, port=0):
if SHOW_WRITES_AND_READS:
print('start_socket')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 0))
s.bind(('', port))
self.port = s.getsockname()[1]
s.listen(1)
if SHOW_WRITES_AND_READS:
print('Waiting in socket.accept()')
self.server_socket = s
newSock, addr = s.accept()
if SHOW_WRITES_AND_READS:
print('Test Writer Thread Socket:', newSock, addr)
@@ -359,7 +399,7 @@ class AbstractWriterThread(threading.Thread):
thread_id = splitted[3]
return thread_id
def wait_for_breakpoint_hit(self, reason='111', get_line=False):
def wait_for_breakpoint_hit(self, reason='111', get_line=False, get_name=False):
'''
108 is over
109 is return
@@ -375,22 +415,29 @@ class AbstractWriterThread(threading.Thread):
last = self.reader_thread.last_received
if i >= 10:
raise AssertionError('After %s seconds, a break with reason: %s was not hit. Found: %s' % \
(i, reason, last))
(i, reason, last))
# we have something like <xml><thread id="12152656" stop_reason="111"><frame id="12453120" ...
# we have something like <xml><thread id="12152656" stop_reason="111"><frame id="12453120" name="encode" ...
splitted = last.split('"')
thread_id = splitted[1]
frameId = splitted[7]
name = splitted[9]
if get_line:
self.log.append('End(0): wait_for_breakpoint_hit')
self.log.append('End(0): wait_for_breakpoint_hit: %s' % (last,))
try:
return thread_id, frameId, int(splitted[13])
if not get_name:
return thread_id, frameId, int(splitted[13])
else:
return thread_id, frameId, int(splitted[13]), name
except:
raise AssertionError('Error with: %s, %s, %s.\nLast: %s.\n\nAll: %s\n\nSplitted: %s' % (
thread_id, frameId, splitted[13], last, '\n'.join(self.reader_thread.all_received), splitted))
self.log.append('End(1): wait_for_breakpoint_hit')
return thread_id, frameId
self.log.append('End(1): wait_for_breakpoint_hit: %s' % (last,))
if not get_name:
return thread_id, frameId
else:
return thread_id, frameId, name
def wait_for_custom_operation(self, expected):
i = 0
@@ -401,7 +448,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, the custom operation not received. Last found:\n%s\nExpected (encoded)\n%s' %
(i, self.reader_thread.last_received, expectedEncoded))
(i, self.reader_thread.last_received, expectedEncoded))
return True
@@ -417,7 +464,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, the vars were not found. Last found:\n%s' %
(i, self.reader_thread.last_received))
(i, self.reader_thread.last_received))
return True
@@ -461,7 +508,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, %s. Last found:\n%s' %
(i, error_msg, last))
(i, error_msg, last))
return True
@@ -479,7 +526,7 @@ class AbstractWriterThread(threading.Thread):
time.sleep(1)
if i >= 10:
raise AssertionError('After %s seconds, the vars were not found. Last found:\n%s' %
(i, self.reader_thread.last_received))
(i, self.reader_thread.last_received))
return True
@@ -490,12 +537,15 @@ class AbstractWriterThread(threading.Thread):
def write_version(self):
self.write("501\t%s\t1.0\tWINDOWS\tID" % self.next_seq())
def get_main_filename(self):
return self.TEST_FILE
def write_add_breakpoint(self, line, func):
'''
@param line: starts at 1
'''
breakpoint_id = self.next_breakpoint_id()
self.write("111\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % (self.next_seq(), breakpoint_id, 'python-line', self.TEST_FILE, line, func))
self.write("111\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % (self.next_seq(), breakpoint_id, 'python-line', self.get_main_filename(), line, func))
self.log.append('write_add_breakpoint: %s line: %s func: %s' % (breakpoint_id, line, func))
return breakpoint_id
@@ -504,7 +554,7 @@ class AbstractWriterThread(threading.Thread):
self.log.append('write_add_exception_breakpoint: %s' % (exception,))
def write_remove_breakpoint(self, breakpoint_id):
self.write("112\t%s\t%s\t%s\t%s" % (self.next_seq(), 'python-line', self.TEST_FILE, breakpoint_id))
self.write("112\t%s\t%s\t%s\t%s" % (self.next_seq(), 'python-line', self.get_main_filename(), breakpoint_id))
def write_change_variable(self, thread_id, frame_id, varname, value):
self.write("117\t%s\t%s\t%s\t%s\t%s\t%s" % (self.next_seq(), thread_id, frame_id, 'FRAME', varname, value))
@@ -82,6 +82,30 @@ class WriterThreadPerformance4(PerformanceWriterThread):
self.write_make_initial_run()
self.finished_ok = True
class WriterThreadPerformance5(PerformanceWriterThread):
TEST_FILE = debugger_unittest._get_debugger_test_file('_performance_2.py')
BENCHMARK_NAME = 'global_scope_1_with_breakpoint'
def run(self):
self.start_socket()
self.write_add_breakpoint(23, None)
self.write_make_initial_run()
self.finished_ok = True
class WriterThreadPerformance6(PerformanceWriterThread):
TEST_FILE = debugger_unittest._get_debugger_test_file('_performance_3.py')
BENCHMARK_NAME = 'global_scope_2_with_breakpoint'
def run(self):
self.start_socket()
self.write_add_breakpoint(17, None)
self.write_make_initial_run()
self.finished_ok = True
class CheckDebuggerPerformance(debugger_unittest.DebuggerRunner):
@@ -95,13 +119,19 @@ class CheckDebuggerPerformance(debugger_unittest.DebuggerRunner):
return float(time_taken)
def obtain_results(self, writer_thread_class):
time_when_debugged = self._get_time_from_result(self.check_case(writer_thread_class))
runs = 5
all_times = []
for _ in range(runs):
all_times.append(self._get_time_from_result(self.check_case(writer_thread_class)))
print('partial for: %s: %.3fs' % (writer_thread_class.BENCHMARK_NAME, all_times[-1]))
all_times.remove(min(all_times))
all_times.remove(max(all_times))
time_when_debugged = sum(all_times) / float(len(all_times))
args = self.get_command_line()
args.append(writer_thread_class.TEST_FILE)
regular_time = self._get_time_from_result(self.run_process(args, writer_thread=None))
simple_trace_time = self._get_time_from_result(self.run_process(args+['--regular-trace'], writer_thread=None))
print(writer_thread_class.BENCHMARK_NAME, time_when_debugged, regular_time, simple_trace_time)
# regular_time = self._get_time_from_result(self.run_process(args, writer_thread=None))
# simple_trace_time = self._get_time_from_result(self.run_process(args+['--regular-trace'], writer_thread=None))
if 'SPEEDTIN_AUTHORIZATION_KEY' in os.environ:
@@ -133,43 +163,62 @@ class CheckDebuggerPerformance(debugger_unittest.DebuggerRunner):
commit_id, branch, commit_date = api.git_commit_id_branch_and_date_from_path(pydevd.__file__)
api.add_benchmark(benchmark_name)
api.add_measurement(
benchmark_name,
value=time_when_debugged,
version=version,
released=False,
branch=branch,
commit_id=commit_id,
commit_date=commit_date,
benchmark_name,
value=time_when_debugged,
version=version,
released=False,
branch=branch,
commit_id=commit_id,
commit_date=commit_date,
)
api.commit()
return '%s: %.3fs ' % (writer_thread_class.BENCHMARK_NAME, time_when_debugged)
def check_performance1(self):
self.obtain_results(WriterThreadPerformance1)
return self.obtain_results(WriterThreadPerformance1)
def check_performance2(self):
self.obtain_results(WriterThreadPerformance2)
return self.obtain_results(WriterThreadPerformance2)
def check_performance3(self):
self.obtain_results(WriterThreadPerformance3)
return self.obtain_results(WriterThreadPerformance3)
def check_performance4(self):
self.obtain_results(WriterThreadPerformance4)
return self.obtain_results(WriterThreadPerformance4)
def check_performance5(self):
return self.obtain_results(WriterThreadPerformance5)
def check_performance6(self):
return self.obtain_results(WriterThreadPerformance6)
if __name__ == '__main__':
debugger_unittest.SHOW_WRITES_AND_READS = False
debugger_unittest.SHOW_OTHER_DEBUG_INFO = False
debugger_unittest.SHOW_STDOUT = False
import time
start_time = time.time()
msgs = []
for check in (
# CHECK_BASELINE, -- Checks against the version checked out at X:\PyDev.Debugger.baseline.
CHECK_REGULAR,
CHECK_CYTHON
):
PerformanceWriterThread.CHECK = check
print('Checking: %s' % (check,))
msgs.append('Checking: %s' % (check,))
check_debugger_performance = CheckDebuggerPerformance()
check_debugger_performance.check_performance1()
check_debugger_performance.check_performance2()
check_debugger_performance.check_performance3()
check_debugger_performance.check_performance4()
msgs.append(check_debugger_performance.check_performance1())
msgs.append(check_debugger_performance.check_performance2())
msgs.append(check_debugger_performance.check_performance3())
msgs.append(check_debugger_performance.check_performance4())
msgs.append(check_debugger_performance.check_performance5())
msgs.append(check_debugger_performance.check_performance6())
for msg in msgs:
print(msg)
print('TotalTime for profile: %.2fs' % (time.time()-start_time,))
@@ -20,35 +20,6 @@ except:
# TestCase
#=======================================================================================================================
class TestCase(unittest.TestCase):
'''
Used for profiling the PyDBAdditionalThreadInfoWithoutCurrentFramesSupport version
'''
def test_met_no_frames_support(self):
from _pydevd_bundle.pydevd_additional_thread_info_regular import PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
info = PyDBAdditionalThreadInfoWithoutCurrentFramesSupport()
main_debugger = Null()
filename = ''
base = ''
additional_info = Null()
t = Null()
frame = Null()
times = 10
for i in range(times):
info.create_db_frame((main_debugger, filename, additional_info, t, frame))
#we haven't kept any reference, so, they must have been garbage-collected already!
self.assertEqual(0, len(info.iter_frames(t)))
kept_frames = []
for i in range(times):
kept_frames.append(info.create_db_frame((main_debugger, filename, additional_info, t, frame)))
for i in range(times):
self.assertEqual(times, len(info.iter_frames(t)))
def test_start_new_thread(self):
pydev_monkey.patch_thread_modules()
@@ -5,7 +5,7 @@
Note that it's a python script but it'll spawn a process to run as jython, ironpython and as python.
'''
from tests_python.debugger_unittest import get_free_port
from tests_pydevd_python.debugger_unittest import get_free_port
import threading
@@ -17,12 +17,16 @@ IRONPYTHON_EXE = None
JYTHON_JAR_LOCATION = None
JAVA_LOCATION = None
try:
xrange
except:
xrange = range
import unittest
import os
import sys
import time
from tests_python import debugger_unittest
from tests_pydevd_python import debugger_unittest
TEST_DJANGO = False
if sys.version_info[:2] == (2, 7):
@@ -239,10 +243,13 @@ class WriterThreadCase17a(debugger_unittest.AbstractWriterThread):
self.write_make_initial_run()
thread_id, frame_id, line = self.wait_for_breakpoint_hit('111', True)
assert line == 2, 'Expected return to be in line 2, was: %s' % line
self.write_step_in(thread_id)
thread_id, frame_id, line = self.wait_for_breakpoint_hit('107', True)
thread_id, frame_id, line, name = self.wait_for_breakpoint_hit('107', get_line=True, get_name=True)
# Should Skip step into properties setter
assert name == 'm3'
assert line == 10, 'Expected return to be in line 10, was: %s' % line
self.write_run_thread(thread_id)
@@ -284,15 +291,17 @@ class WriterThreadCase16(debugger_unittest.AbstractWriterThread):
self.wait_for_var('<var name="%27size%27')
self.write_get_variable(thread_id, frame_id, 'bigarray')
# isContainer could be true on some numpy versions, so, we only check for the var begin.
self.wait_for_var([
'<var name="min" type="int64" qualifier="numpy" value="int64%253A 0" />',
'<var name="min" type="int64" qualifier="numpy" value="int64%3A 0" />',
'<var name="size" type="int" qualifier="{}" value="int%3A 100000" />'.format(builtin_qualifier),
'<var name="min" type="int64" qualifier="numpy" value="int64%253A 0"',
'<var name="min" type="int64" qualifier="numpy" value="int64%3A 0"',
'<var name="size" type="int" qualifier="{}" value="int%3A 100000"'.format(builtin_qualifier),
])
self.wait_for_var([
'<var name="max" type="int64" qualifier="numpy" value="int64%253A 99999" />',
'<var name="max" type="int32" qualifier="numpy" value="int32%253A 99999" />',
'<var name="max" type="int64" qualifier="numpy" value="int64%3A 99999"'
'<var name="max" type="int64" qualifier="numpy" value="int64%253A 99999"',
'<var name="max" type="int32" qualifier="numpy" value="int32%253A 99999"',
'<var name="max" type="int64" qualifier="numpy" value="int64%3A 99999"',
'<var name="max" type="int32" qualifier="numpy" value="int32%253A 99999"',
])
self.wait_for_var('<var name="shape" type="tuple"')
self.wait_for_var('<var name="dtype" type="dtype"')
@@ -306,10 +315,14 @@ class WriterThreadCase16(debugger_unittest.AbstractWriterThread):
self.wait_for_var([
'<var name="min" type="str" qualifier={} value="str%253A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
'<var name="min" type="str" qualifier={} value="str%3A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
'<var name="min" type="str" qualifier="{}" value="str%253A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
'<var name="min" type="str" qualifier="{}" value="str%3A ndarray too big%252C calculating min would slow down debugging" />'.format(builtin_qualifier),
])
self.wait_for_var([
'<var name="max" type="str" qualifier={} value="str%253A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
'<var name="max" type="str" qualifier={} value="str%3A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
'<var name="max" type="str" qualifier="{}" value="str%253A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
'<var name="max" type="str" qualifier="{}" value="str%3A ndarray too big%252C calculating max would slow down debugging" />'.format(builtin_qualifier),
])
self.wait_for_var('<var name="shape" type="tuple"')
self.wait_for_var('<var name="dtype" type="dtype"')
@@ -487,7 +500,9 @@ class WriterThreadCase11(debugger_unittest.AbstractWriterThread):
self.write_add_breakpoint(2, 'Method1')
self.write_make_initial_run()
thread_id, frame_id = self.wait_for_breakpoint_hit('111')
thread_id, frame_id, line = self.wait_for_breakpoint_hit('111', True)
assert line == 2, 'Expected return to be in line 2, was: %s' % line
self.write_step_over(thread_id)
@@ -877,6 +892,37 @@ class WriterThreadCaseQThread3(debugger_unittest.AbstractWriterThread):
self.log.append('Marking finished ok.')
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseQThread4
#=======================================================================================================================
class WriterThreadCaseQThread4(debugger_unittest.AbstractWriterThread):
TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_qthread4.py')
def run(self):
self.start_socket()
breakpoint_id = self.write_add_breakpoint(24, 'on_start')
self.write_make_initial_run()
thread_id, frame_id = self.wait_for_breakpoint_hit()
self.write_remove_breakpoint(breakpoint_id)
self.write_run_thread(thread_id)
self.log.append('Checking sequence. Found: %s' % (self._sequence))
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
self.log.append('Marking finished ok.')
self.finished_ok = True
def additional_output_checks(self, stdout, stderr):
if 'On start called' not in stdout:
raise AssertionError('Expected "On start called" to be in stdout:\n%s' % (stdout,))
if 'Done sleeping' not in stdout:
raise AssertionError('Expected "Done sleeping" to be in stdout:\n%s' % (stdout,))
if 'native Qt signal is not callable' in stderr:
raise AssertionError('Did not expect "native Qt signal is not callable" to be in stderr:\n%s' % (stderr,))
#=======================================================================================================================
# WriterThreadCase1
#=======================================================================================================================
@@ -918,6 +964,162 @@ class WriterThreadCase1(debugger_unittest.AbstractWriterThread):
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseMSwitch
#=======================================================================================================================
class WriterThreadCaseMSwitch(debugger_unittest.AbstractWriterThread):
TEST_FILE = 'tests_pydevd_python._debugger_case_m_switch'
IS_MODULE = True
def get_environ(self):
env = os.environ.copy()
curr_pythonpath = env.get('PYTHONPATH', '')
root_dirname = os.path.dirname(os.path.dirname(__file__))
curr_pythonpath += root_dirname + os.pathsep
env['PYTHONPATH'] = curr_pythonpath
return env
def get_main_filename(self):
return debugger_unittest._get_debugger_test_file('_debugger_case_m_switch.py')
def run(self):
self.start_socket()
self.log.append('writing add breakpoint')
breakpoint_id = self.write_add_breakpoint(1, None)
self.log.append('making initial run')
self.write_make_initial_run()
self.log.append('waiting for breakpoint hit')
thread_id, frame_id = self.wait_for_breakpoint_hit()
self.write_remove_breakpoint(breakpoint_id)
self.log.append('run thread')
self.write_run_thread(thread_id)
self.log.append('asserting')
try:
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
except:
self.log.append('assert failed!')
raise
self.log.append('asserted')
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseRemoteDebugger
#=======================================================================================================================
class WriterThreadCaseRemoteDebugger(debugger_unittest.AbstractWriterThread):
TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_remote.py')
def run(self):
self.start_socket(8787)
self.log.append('making initial run')
self.write_make_initial_run()
self.log.append('waiting for breakpoint hit')
thread_id, frame_id = self.wait_for_breakpoint_hit('105')
self.log.append('run thread')
self.write_run_thread(thread_id)
self.log.append('asserting')
try:
assert 5 == self._sequence, 'Expected 5. Had: %s' % self._sequence
except:
self.log.append('assert failed!')
raise
self.log.append('asserted')
self.finished_ok = True
#=======================================================================================================================
# _SecondaryMultiProcProcessWriterThread
#=======================================================================================================================
class _SecondaryMultiProcProcessWriterThread(debugger_unittest.AbstractWriterThread):
FORCE_KILL_PROCESS_WHEN_FINISHED_OK = True
def __init__(self, server_socket):
debugger_unittest.AbstractWriterThread.__init__(self)
self.server_socket = server_socket
def run(self):
print('waiting for second process')
self.sock, addr = self.server_socket.accept()
print('accepted second process')
from tests_pydevd_python.debugger_unittest import ReaderThread
self.reader_thread = ReaderThread(self.sock)
self.reader_thread.start()
self._sequence = -1
# initial command is always the version
self.write_version()
self.log.append('start_socket')
self.write_make_initial_run()
time.sleep(.5)
self.finished_ok = True
#=======================================================================================================================
# WriterThreadCaseRemoteDebuggerMultiProc
#=======================================================================================================================
class WriterThreadCaseRemoteDebuggerMultiProc(debugger_unittest.AbstractWriterThread):
# It seems sometimes it becomes flaky on the ci because the process outlives the writer thread...
# As we're only interested in knowing if a second connection was received, just kill the related
# process.
FORCE_KILL_PROCESS_WHEN_FINISHED_OK = True
TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_remote_1.py')
def run(self):
self.start_socket(8787)
self.log.append('making initial run')
self.write_make_initial_run()
self.log.append('waiting for breakpoint hit')
thread_id, frame_id = self.wait_for_breakpoint_hit('105')
self.secondary_multi_proc_process_writer_thread = secondary_multi_proc_process_writer_thread = \
_SecondaryMultiProcProcessWriterThread(self.server_socket)
secondary_multi_proc_process_writer_thread.start()
self.log.append('run thread')
self.write_run_thread(thread_id)
for _i in xrange(400):
if secondary_multi_proc_process_writer_thread.finished_ok:
break
time.sleep(.1)
else:
self.log.append('Secondary process not finished ok!')
raise AssertionError('Secondary process not finished ok!')
self.log.append('Secondary process finished!')
try:
assert 5 == self._sequence, 'Expected 5. Had: %s' % self._sequence
except:
self.log.append('assert failed!')
raise
self.log.append('asserted')
self.finished_ok = True
def do_kill(self):
debugger_unittest.AbstractWriterThread.do_kill(self)
if hasattr(self, 'secondary_multi_proc_process_writer_thread'):
self.secondary_multi_proc_process_writer_thread.do_kill()
#=======================================================================================================================
# DebuggerBase
#=======================================================================================================================
@@ -1016,6 +1218,29 @@ class DebuggerBase(debugger_unittest.DebuggerRunner):
if self._has_qt():
self.check_case(WriterThreadCaseQThread3)
def test_case_qthread4(self):
if self._has_qt():
self.check_case(WriterThreadCaseQThread4)
def test_m_switch(self):
self.check_case(WriterThreadCaseMSwitch)
# class TestPythonRemoteDebugger(unittest.TestCase, debugger_unittest.DebuggerRunner):
#
# def get_command_line(self):
# return [PYTHON_EXE, '-u']
#
# def add_command_line_args(self, args):
# return args + [self.writer_thread.TEST_FILE]
#
# def test_remote_debugger(self):
# self.check_case(WriterThreadCaseRemoteDebugger)
#
# def test_remote_debugger2(self):
# self.check_case(WriterThreadCaseRemoteDebuggerMultiProc)
class TestPython(unittest.TestCase, DebuggerBase):
def get_command_line(self):
@@ -1154,7 +1379,7 @@ if __name__ == '__main__':
#
# suite.addTests(unittest.makeSuite(TestIronPython))
#
suite.addTests(unittest.makeSuite(TestPython))
# suite.addTests(unittest.makeSuite(TestPython))
@@ -1178,6 +1403,6 @@ if __name__ == '__main__':
# unittest.TextTestRunner(verbosity=3).run(suite)
# suite.addTest(TestPython('test_case_17'))
# suite.addTest(TestPython('test_case_18'))
# suite.addTest(TestPython('test_case_19'))
suite.addTest(TestPython('test_case_qthread4'))
unittest.TextTestRunner(verbosity=3).run(suite)
@@ -0,0 +1,28 @@
import unittest
class Test(unittest.TestCase):
def testProcessCommandLine(self):
from _pydevd_bundle.pydevd_command_line_handling import process_command_line, setup_to_argv
setup = process_command_line(['pydevd.py', '--port', '1', '--save-threading'])
assert setup['save-threading']
assert setup['port'] == 1
assert not setup['qt-support']
argv = setup_to_argv(setup)
assert argv[0].endswith('pydevd.py')
argv = argv[1:]
assert argv == ['--port', '1', '--save-threading']
def testProcessCommandLine2(self):
from _pydevd_bundle.pydevd_command_line_handling import process_command_line, setup_to_argv
setup = process_command_line(['pydevd.py', '--port', '1', '--qt-support=auto'])
assert setup['qt-support'] == 'auto'
setup = process_command_line(['pydevd.py', '--port', '1', '--qt-support'])
assert setup['qt-support'] == 'auto'
setup = process_command_line(['pydevd.py', '--port', '1', '--qt-support=pyqt4'])
assert setup['qt-support'] == 'pyqt4'
self.assertRaises(ValueError, process_command_line, ['pydevd.py', '--port', '1', '--qt-support=wrong'])
@@ -19,14 +19,13 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check='''C:\\bin\\python.exe -u -c connect(\\"127.0.0.1\\")'''
sys.original_argv = []
debug_command = (
'import sys; '
'sys.path.append(r\'%s\'); '
'import pydevd; pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, '
'trace_only_current_thread=False, patch_multiprocessing=True); '
'sys.original_argv = []; '
'connect("127.0.0.1")') % pydev_src_dir
'import sys; '
'sys.path.append(r\'%s\'); '
'import pydevd; pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, '
'trace_only_current_thread=False, patch_multiprocessing=True); '
''
'connect("127.0.0.1")') % pydev_src_dir
if sys.platform == "win32":
debug_command = debug_command.replace('"', '\\"')
debug_command = '"%s"' % debug_command
@@ -45,12 +44,11 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', '-u', '-c', 'connect("127.0.0.1")']
sys.original_argv = []
debug_command = (
'import sys; sys.path.append(r\'%s\'); import pydevd; '
'pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); '
'sys.original_argv = []; '
'connect("127.0.0.1")') % pydev_src_dir
'import sys; sys.path.append(r\'%s\'); import pydevd; '
'pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); '
''
'connect("127.0.0.1")') % pydev_src_dir
if sys.platform == "win32":
debug_command = debug_command.replace('"', '\\"')
debug_command = '"%s"' % debug_command
@@ -68,14 +66,19 @@ class TestCase(unittest.TestCase):
original = SetupHolder.setup
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0', 'multiprocess': True}
check=['C:\\bin\\python.exe', '-m', 'test']
sys.original_argv = ['pydevd', '--multiprocess']
from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe',
'pydevd',
get_pydevd_file(),
'--module',
'--port',
'0',
'--client',
'127.0.0.1',
'--multiprocess',
'--file',
'test',
])
finally:
@@ -87,9 +90,16 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', 'connect(\\"127.0.0.1\\")']
sys.original_argv = ['my', 'original', 'argv']
from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe', 'my', 'original', 'argv', 'connect(\\"127.0.0.1\\")'])
'C:\\bin\\python.exe',
get_pydevd_file(),
'--port',
'0',
'--client',
'127.0.0.1',
'--file',
'connect(\\"127.0.0.1\\")'])
finally:
SetupHolder.setup = original
@@ -99,7 +109,6 @@ class TestCase(unittest.TestCase):
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', 'pydevd.py', 'connect(\\"127.0.0.1\\")', 'bar']
sys.original_argv = ['my', 'original', 'argv']
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe', 'pydevd.py', 'connect(\\"127.0.0.1\\")', 'bar'])
@@ -108,18 +117,19 @@ class TestCase(unittest.TestCase):
def test_monkey_patch_args_no_indc_without_pydevd(self):
original = SetupHolder.setup
from _pydevd_bundle.pydevd_command_line_handling import get_pydevd_file
try:
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
check=['C:\\bin\\python.exe', 'target.py', 'connect(\\"127.0.0.1\\")', 'bar']
sys.original_argv = ['pydevd.py', '--a=1', 'b', '--c=2', '--file', 'ignore_this.py']
self.assertEqual(pydev_monkey.patch_args(check), [
'C:\\bin\\python.exe',
'pydevd.py',
'--a=1',
'b',
'--c=2',
get_pydevd_file(),
'--port',
'0',
'--client',
'127.0.0.1',
'--file',
'target.py',
'connect(\\"127.0.0.1\\")',
+5
View File
@@ -1,7 +1,12 @@
from time import sleep
def foo(x):
print(x)
x = 0
print(x)
foo(x)
while x<2:
x+=1
print(x)
@@ -379,15 +379,15 @@ public class PythonDebuggerTest extends PyEnvTestCase {
runPythonTest(new PyDebuggerTask("/debug", "test_runtoline.py") {
@Override
public void before() throws Exception {
toggleBreakpoint(getFilePath(getScriptName()), 3);
toggleBreakpoint(getFilePath(getScriptName()), 9);
toggleBreakpoint(getFilePath(getScriptName()), 7);
toggleBreakpoint(getFilePath(getScriptName()), 14);
}
@Override
public void testing() throws Exception {
waitForPause();
eval("x").hasValue("0");
runToLine(6);
runToLine(11);
eval("x").hasValue("1");
resume();
waitForPause();
@@ -961,8 +961,7 @@ public class PythonDebuggerTest extends PyEnvTestCase {
runPythonTest(new PyDebuggerTask("/debug", "test_return_values.py") {
@Override
public void before() throws Exception {
toggleBreakpoint(getScriptName(), 7);
toggleBreakpoint(getScriptName(), 11);
toggleBreakpoint(getScriptName(), 2);
final PyDebuggerSettings debuggerSettings = PyDebuggerSettings.getInstance();
debuggerSettings.setWatchReturnValues(true);
}
@@ -975,9 +974,13 @@ public class PythonDebuggerTest extends PyEnvTestCase {
@Override
public void testing() throws Exception {
waitForPause();
stepOver();
waitForPause();
eval(PyDebugValue.RETURN_VALUES_PREFIX + "['bar'][0]").hasValue("1");
resume();
stepOver();
waitForPause();
stepOver();
waitForPause();
eval(PyDebugValue.RETURN_VALUES_PREFIX + "['foo']").hasValue("33");
resume();
@@ -986,7 +989,7 @@ public class PythonDebuggerTest extends PyEnvTestCase {
@NotNull
@Override
public Set<String> getTags() {
return ImmutableSet.of("-iron");
return ImmutableSet.of("-iron", "-python36");
}
});
}
@@ -1155,6 +1158,7 @@ public class PythonDebuggerTest extends PyEnvTestCase {
@Override
public void before() throws Exception {
toggleBreakpoint(getScriptName(), 2);
toggleBreakpoint(getScriptName(), 3);
}
@Override
@@ -1164,6 +1168,9 @@ public class PythonDebuggerTest extends PyEnvTestCase {
toggleBreakpoint(getScriptName(), 5);
resume();
waitForPause();
eval("b").hasValue("2");
resume();
waitForPause();
eval("d").hasValue("4");
resume();
}