mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-18 20:41:22 +07:00
[python] PY-86999: Survive broken pyproject.toml.
`com.intellij.python.pyproject.PyProjectToml.Companion.parse` used to return Error in case of broken file so we simply ignored it. We now return `TomlTable` itself even in case of error. So `com.intellij.python.pyproject.model.internal.pyProjectToml.TomFileToolsKt.readFile` is only null (which means module is skipped) if there is `IOException` reading `pyproject.toml` (either file doesn't exist or unreadable). GitOrigin-RevId: 94fa7f46516450a9b0922679aa0d9d12571fae4f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0ed3c5e373
commit
8c0deced79
@@ -56,6 +56,7 @@ jvm_library(
|
||||
associates = [":pyproject"],
|
||||
deps = [
|
||||
"@lib//:kotlin-stdlib",
|
||||
"//libraries/assertj-core",
|
||||
"//python/openapi:community",
|
||||
"//python/openapi:community_test_lib",
|
||||
"//python/python-psi-impl:psi-impl",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="kotlin-stdlib" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.libraries.assertj.core" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.python.community" />
|
||||
<orderEntry type="module" module-name="intellij.python.psi.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core" />
|
||||
|
||||
@@ -4,12 +4,12 @@ package com.intellij.python.pyproject
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.Result.Companion.success
|
||||
import com.jetbrains.python.sdk.findAmongRoots
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.tuweni.toml.Toml
|
||||
import org.apache.tuweni.toml.TomlParseError
|
||||
import org.apache.tuweni.toml.TomlParseResult
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import java.nio.file.Path
|
||||
@@ -78,7 +78,7 @@ data class PyProjectToml(
|
||||
/**
|
||||
* An instance of [TomlTable] provided by the TOML parser.
|
||||
*/
|
||||
val toml: TomlTable,
|
||||
val toml: TomlParseResult,
|
||||
) {
|
||||
/**
|
||||
* Gets a specific tool from an object implementing [PyProjectToolFactory].
|
||||
@@ -103,9 +103,10 @@ data class PyProjectToml(
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* TODO: REDOC
|
||||
* Attempts to parse [inputStream] and construct an instance of [PyProjectToml].
|
||||
* On success, returns an instance of [Result.Success] with an instance of [PyProjectToml].
|
||||
* On failure, returns an instance of [Result.Failure] with a list of [TomlParseError]s.
|
||||
* On failure, returns an instance of [Result.Failure] with a list of [TomlParseError]s and [TomlTable] itself.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
@@ -115,18 +116,15 @@ data class PyProjectToml(
|
||||
* val hatch = pyProject.getTool(HatchPyProject)
|
||||
* ```
|
||||
*/
|
||||
fun parse(tomlFileContent: String): Result<PyProjectToml, List<TomlParseError>> {
|
||||
fun parse(tomlFileContent: String): PyProjectToml {
|
||||
val issues = mutableListOf<PyProjectIssue>()
|
||||
val toml = Toml.parse(tomlFileContent)
|
||||
|
||||
if (toml.hasErrors()) {
|
||||
return Result.failure(toml.errors())
|
||||
}
|
||||
|
||||
val projectTable = toml.safeGet<TomlTable>(PY_PROJECT_TOML_PROJECT).getOrIssue(issues)
|
||||
|
||||
if (projectTable == null) {
|
||||
return success(PyProjectToml(null, issues, toml))
|
||||
return PyProjectToml(null, issues, toml)
|
||||
}
|
||||
|
||||
val name = projectTable.safeGet<String>("name").getOrIssue(issues) {
|
||||
@@ -205,33 +203,31 @@ data class PyProjectToml(
|
||||
val guiScripts = projectTable.parseMap("gui-scripts", issues)
|
||||
val urls = projectTable.parseMap("urls", issues)
|
||||
|
||||
return success(
|
||||
PyProjectToml(
|
||||
PyProjectTable(
|
||||
name,
|
||||
version,
|
||||
requiresPython,
|
||||
authors,
|
||||
maintainers,
|
||||
description,
|
||||
readme,
|
||||
license,
|
||||
licenseFiles,
|
||||
keywords,
|
||||
classifiers,
|
||||
dynamic,
|
||||
PyProjectDependencies(
|
||||
projectDependencies,
|
||||
devDependencies,
|
||||
optionalDependencies
|
||||
),
|
||||
scripts,
|
||||
guiScripts,
|
||||
urls,
|
||||
return PyProjectToml(
|
||||
PyProjectTable(
|
||||
name,
|
||||
version,
|
||||
requiresPython,
|
||||
authors,
|
||||
maintainers,
|
||||
description,
|
||||
readme,
|
||||
license,
|
||||
licenseFiles,
|
||||
keywords,
|
||||
classifiers,
|
||||
dynamic,
|
||||
PyProjectDependencies(
|
||||
projectDependencies,
|
||||
devDependencies,
|
||||
optionalDependencies
|
||||
),
|
||||
issues,
|
||||
toml,
|
||||
)
|
||||
scripts,
|
||||
guiScripts,
|
||||
urls,
|
||||
),
|
||||
issues,
|
||||
toml,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -110,12 +110,13 @@ private suspend fun readFile(file: Path): PyProjectToml? {
|
||||
logger.warn("Can't read $file", e)
|
||||
return null
|
||||
}
|
||||
return when (val r = withContext(Dispatchers.Default) { PyProjectToml.parse(content) }) {
|
||||
is Result.Failure -> {
|
||||
logger.warn("Errors on $file: ${r.error.joinToString(", ")}")
|
||||
null
|
||||
return withContext(Dispatchers.Default) {
|
||||
val toml = PyProjectToml.parse(content)
|
||||
val errors = toml.issues.joinToString(", ")
|
||||
if (errors.isNotBlank()) {
|
||||
logger.warn("Errors on $file: $errors")
|
||||
}
|
||||
is Result.Success -> r.result
|
||||
toml
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,13 +132,14 @@ private suspend fun generatePyProjectTomlEntries(
|
||||
participatedTools.add(toolAndName.first.id)
|
||||
}
|
||||
}
|
||||
if (projectNameAsString != null) {
|
||||
if (projectNameAsString in usedNamed) {
|
||||
projectNameAsString = "$projectNameAsString@${usedNamed.size}"
|
||||
}
|
||||
usedNamed.add(projectNameAsString)
|
||||
if (projectNameAsString == null) {
|
||||
projectNameAsString = root.name
|
||||
}
|
||||
val projectName = ProjectName(projectNameAsString ?: "${root.name}@${tomlFile.hashCode()}")
|
||||
if (projectNameAsString in usedNamed) {
|
||||
projectNameAsString = "$projectNameAsString@${usedNamed.size}"
|
||||
}
|
||||
usedNamed.add(projectNameAsString)
|
||||
val projectName = ProjectName(projectNameAsString)
|
||||
val sourceRootsAndTools = Tool.EP.extensionList.flatMap { tool -> tool.getSrcRoots(toml.toml, root).map { Pair(tool, it) } }.toSet()
|
||||
val sourceRoots = sourceRootsAndTools.map { it.second }.toSet() + findSrc(root)
|
||||
participatedTools.addAll(sourceRootsAndTools.map { it.first.id })
|
||||
|
||||
@@ -4,11 +4,9 @@ import com.intellij.python.pyproject.*
|
||||
import com.intellij.python.pyproject.PyProjectIssue.*
|
||||
import com.intellij.python.pyproject.TomlTableSafeGetError.RequiredValueMissing
|
||||
import com.intellij.python.pyproject.TomlTableSafeGetError.UnexpectedType
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.getOrThrow
|
||||
import com.jetbrains.python.isFailure
|
||||
import org.apache.tuweni.toml.TomlArray
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
@@ -26,8 +24,7 @@ class PyProjectTomlTest {
|
||||
val result = PyProjectToml.Companion.parse(configContents)
|
||||
|
||||
// THEN
|
||||
assert(result.isFailure)
|
||||
assert((result as Result.Failure).error.isNotEmpty())
|
||||
Assertions.assertThat(result.toml.errors()).isNotEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -45,7 +42,7 @@ class PyProjectTomlTest {
|
||||
bar="test bar"
|
||||
baz="test baz"
|
||||
""".trimIndent()
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents).orThrow()
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents)
|
||||
|
||||
// WHEN
|
||||
val testTool = pyproject.getTool(TestPyProject)
|
||||
@@ -69,7 +66,7 @@ class PyProjectTomlTest {
|
||||
""".trimIndent()
|
||||
|
||||
// WHEN
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents).orThrow()
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents)
|
||||
val testTool = pyproject.getTool(TestPyProject)
|
||||
|
||||
// THEN
|
||||
@@ -95,7 +92,7 @@ class PyProjectTomlTest {
|
||||
""".trimIndent()
|
||||
|
||||
// WHEN
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents).orThrow()
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents)
|
||||
val testTool = pyproject.getTool(TestPyProject)
|
||||
|
||||
// THEN
|
||||
@@ -114,7 +111,7 @@ class PyProjectTomlTest {
|
||||
name="Some project"
|
||||
version="1.2.3"
|
||||
""".trimIndent()
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents).orThrow()
|
||||
val pyproject = PyProjectToml.Companion.parse(configContents)
|
||||
|
||||
// WHEN
|
||||
val testTool = pyproject.getTool(TestPyProject)
|
||||
@@ -128,7 +125,7 @@ class PyProjectTomlTest {
|
||||
@MethodSource("parseTestCases")
|
||||
fun parseTests(name: String, pyprojectToml: String, expectedProjectTable: PyProjectTable?, expectedIssues: List<PyProjectIssue>) {
|
||||
val result = PyProjectToml.Companion.parse(pyprojectToml)
|
||||
val unwrapped = result.getOrThrow()
|
||||
val unwrapped = result
|
||||
|
||||
assertEquals(expectedProjectTable, unwrapped.project)
|
||||
assertEquals(expectedIssues, unwrapped.issues)
|
||||
|
||||
Reference in New Issue
Block a user