diff --git a/python/src/com/jetbrains/python/run/PyTracebackParser.kt b/python/src/com/jetbrains/python/run/PyTracebackParser.kt index aabe3df59c67..0cfa2b8bb917 100644 --- a/python/src/com/jetbrains/python/run/PyTracebackParser.kt +++ b/python/src/com/jetbrains/python/run/PyTracebackParser.kt @@ -1,32 +1,36 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.run; +package com.jetbrains.python.run -import com.jetbrains.python.traceBackParsers.LinkInTrace; -import com.jetbrains.python.traceBackParsers.TraceBackParserAdapter; -import org.jetbrains.annotations.NotNull; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.jetbrains.python.traceBackParsers.LinkInTrace +import com.jetbrains.python.traceBackParsers.TraceBackParserAdapter +import java.util.regex.Matcher +import java.util.regex.Pattern /** * Finds links in default python traceback * * @author Ilya.Kazakevich */ -public class PyTracebackParser extends TraceBackParserAdapter { +open class PyTracebackParser : TraceBackParserAdapter(Pattern.compile( + "(File \"(?[^0-9][^\"]{0,200})\", line (?\\d{1,8}))|(File (?[^0-9][^\"]{0,200}):(?\\d{1,8}))")) { + override fun findLinkInTrace(line: String, matchedMatcher: Matcher): LinkInTrace { + val file1 = matchedMatcher.group("file") + val file2 = matchedMatcher.group("file2") + val fileName = (file1 ?: file2).replace('\\', '/') + val lineNumber1 = matchedMatcher.group("line") + val lineNumber2 = matchedMatcher.group("line2") + val lineNumber = (lineNumber1 ?: lineNumber2).toInt() - public PyTracebackParser() { - // File name can't start with number, can't be more then 200 chars long (its insane) and line number is also limited to int maxvalue - super(Pattern.compile("File \"([^0-9][^\"]{0,200})\", line (\\d{1,8})")); + if (file1 != null && lineNumber1 != null) { + val startPos = line.indexOf('\"') + 1 + val endPos = line.indexOf('\"', startPos) + return LinkInTrace(fileName, lineNumber, startPos, endPos) + } + else { + val startPos = matchedMatcher.start("file2") + val endPos = matchedMatcher.end("line2") + return LinkInTrace(fileName, lineNumber, startPos, endPos) + } } - - @Override - protected @NotNull LinkInTrace findLinkInTrace(final @NotNull String line, final @NotNull Matcher matchedMatcher) { - final String fileName = matchedMatcher.group(1).replace('\\', '/'); - final int lineNumber = Integer.parseInt(matchedMatcher.group(2)); - final int startPos = line.indexOf('\"') + 1; - final int endPos = line.indexOf('\"', startPos); - return new LinkInTrace(fileName, lineNumber, startPos, endPos); - } -} +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/run/PythonTracebackFilter.java b/python/src/com/jetbrains/python/run/PythonTracebackFilter.java deleted file mode 100644 index 20bb694ff5e1..000000000000 --- a/python/src/com/jetbrains/python/run/PythonTracebackFilter.java +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.run; - -import com.intellij.execution.filters.Filter; -import com.intellij.execution.filters.OpenFileHyperlinkInfo; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.python.traceBackParsers.LinkInTrace; -import com.jetbrains.python.traceBackParsers.TraceBackParser; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; - - -public class PythonTracebackFilter implements Filter { - private final Project myProject; - private final String myWorkingDirectory; - - public PythonTracebackFilter(final Project project) { - myProject = project; - myWorkingDirectory = project.getBasePath(); - } - - public PythonTracebackFilter(final Project project, final @Nullable String workingDirectory) { - myProject = project; - myWorkingDirectory = workingDirectory; - } - - @Override - public final @Nullable Result applyFilter(final @NotNull String line, final int entireLength) { - - for (final TraceBackParser parser : TraceBackParser.PARSERS) { - final LinkInTrace linkInTrace = parser.findLinkInTrace(line); - if (linkInTrace == null) { - continue; - } - final int lineNumber = linkInTrace.getLineNumber(); - final VirtualFile vFile = findFileByName(linkInTrace.getFileName()); - - if (vFile != null) { - if (!vFile.isDirectory()) { - var extension = vFile.getExtension(); - if (extension != null && !extension.equals("py")) { - return null; - } - } - final OpenFileHyperlinkInfo hyperlink = new OpenFileHyperlinkInfo(myProject, vFile, lineNumber - 1); - final int textStartOffset = entireLength - line.length(); - final int startPos = linkInTrace.getStartPos(); - final int endPos = linkInTrace.getEndPos(); - return new Result(startPos + textStartOffset, endPos + textStartOffset, hyperlink); - } - } - return null; - } - - protected @Nullable VirtualFile findFileByName(final @NotNull String fileName) { - VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fileName); - if (vFile == null && !StringUtil.isEmptyOrSpaces(myWorkingDirectory)) { - vFile = LocalFileSystem.getInstance().findFileByIoFile(new File(myWorkingDirectory, fileName)); - } - return vFile; - } - - protected Project getProject() { - return myProject; - } -} diff --git a/python/src/com/jetbrains/python/run/PythonTracebackFilter.kt b/python/src/com/jetbrains/python/run/PythonTracebackFilter.kt new file mode 100644 index 000000000000..563c043f6b1a --- /dev/null +++ b/python/src/com/jetbrains/python/run/PythonTracebackFilter.kt @@ -0,0 +1,66 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.jetbrains.python.run + +import com.intellij.execution.filters.Filter +import com.intellij.execution.filters.OpenFileHyperlinkInfo +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.SystemProperties +import com.jetbrains.python.traceBackParsers.TraceBackParser +import java.io.File + +open class PythonTracebackFilter : Filter { + protected val project: Project + private val myWorkingDirectory: String? + + constructor(project: Project) { + this.project = project + myWorkingDirectory = project.basePath + } + + constructor(project: Project, workingDirectory: String?) { + this.project = project + myWorkingDirectory = workingDirectory + } + + override fun applyFilter(line: String, entireLength: Int): Filter.Result? { + for (parser in TraceBackParser.PARSERS) { + val linkInTrace = parser.findLinkInTrace(line) + if (linkInTrace == null) { + continue + } + val lineNumber = linkInTrace.lineNumber + val vFile = findFileByName(linkInTrace.fileName) + + if (vFile != null) { + if (!vFile.isDirectory) { + val extension = vFile.extension + if (extension != null && extension != "py") { + return null + } + } + val hyperlink = OpenFileHyperlinkInfo(project, vFile, lineNumber - 1) + val textStartOffset = entireLength - line.length + val startPos = linkInTrace.startPos + val endPos = linkInTrace.endPos + return Filter.Result(startPos + textStartOffset, endPos + textStartOffset, hyperlink) + } + } + return null + } + + protected open fun findFileByName(fileName: String): VirtualFile? { + val preparedName = if (fileName.startsWith("~")) { + fileName.replaceFirst("~".toRegex(), SystemProperties.getUserHome()) + } + else { + fileName + } + var vFile = LocalFileSystem.getInstance().findFileByPath(preparedName) + if (vFile == null && !myWorkingDirectory.isNullOrBlank()) { + vFile = LocalFileSystem.getInstance().findFileByIoFile(File(myWorkingDirectory, preparedName)) + } + return vFile + } +} \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/run/PyTracebackParserTest.java b/python/testSrc/com/jetbrains/python/run/PyTracebackParserTest.java index a3fd10f55fd6..f615455783df 100644 --- a/python/testSrc/com/jetbrains/python/run/PyTracebackParserTest.java +++ b/python/testSrc/com/jetbrains/python/run/PyTracebackParserTest.java @@ -39,6 +39,16 @@ public class PyTracebackParserTest extends TestCase { Assert.assertEquals("Bad end pos", 16, linkInTrace.getEndPos()); } + public void testLineWithLink2() { + final LinkInTrace linkInTrace = new PyTracebackParser().findLinkInTrace( + "File ~/.pyenv/versions/3.10.11/lib/python3.10/threading.py:324, in Condition.wait(self, timeout)"); + Assert.assertNotNull("Failed to parse line", linkInTrace); + Assert.assertEquals("Bad file name", "~/.pyenv/versions/3.10.11/lib/python3.10/threading.py", linkInTrace.getFileName()); + Assert.assertEquals("Bad line number", 324, linkInTrace.getLineNumber()); + Assert.assertEquals("Bad start pos", 5, linkInTrace.getStartPos()); + Assert.assertEquals("Bad end pos", 62, linkInTrace.getEndPos()); + } + /** * lines with out of file references should not have links */