From 0730668e3dc747534282d92f2d10f07689667432 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 16 Dec 2020 18:56:50 +0900 Subject: [PATCH 1/4] Support custom root field --- src/com/koxudaxi/pydantic/Pydantic.kt | 4 +++- src/com/koxudaxi/pydantic/PydanticInspection.kt | 2 +- .../koxudaxi/pydantic/PydanticTypeProvider.kt | 2 +- testData/completion/fieldCustomRoot.py | 7 +++++++ testData/completion/keywordArgumentCustomRoot.py | 12 ++++++++++++ .../koxudaxi/pydantic/PydanticCompletionTest.kt | 16 ++++++++++++++++ 6 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 testData/completion/fieldCustomRoot.py create mode 100644 testData/completion/keywordArgumentCustomRoot.py diff --git a/src/com/koxudaxi/pydantic/Pydantic.kt b/src/com/koxudaxi/pydantic/Pydantic.kt index 8b5d94e1..f62d063a 100644 --- a/src/com/koxudaxi/pydantic/Pydantic.kt +++ b/src/com/koxudaxi/pydantic/Pydantic.kt @@ -101,6 +101,8 @@ val CONFIG_TYPES = mapOf( "keep_untouched" to ConfigType.LIST_PYTYPE ) +const val CUSTOM_ROOT_FIELD = "__root__" + fun getPyClassByPyCallExpression(pyCallExpression: PyCallExpression, includeDataclass: Boolean, context: TypeEvalContext): PyClass? { val callee = pyCallExpression.callee ?: return null val pyType = when (val type = context.getType(callee)) { @@ -290,7 +292,7 @@ fun isValidField(field: PyTargetExpression): Boolean { } fun isValidFieldName(name: String?): Boolean { - return name?.startsWith('_') == false + return name?.let { !it.startsWith('_') || it == CUSTOM_ROOT_FIELD } ?: false } fun getConfigValue(name: String, value: Any?, context: TypeEvalContext): Any? { diff --git a/src/com/koxudaxi/pydantic/PydanticInspection.kt b/src/com/koxudaxi/pydantic/PydanticInspection.kt index 48e6cd69..76141bc7 100644 --- a/src/com/koxudaxi/pydantic/PydanticInspection.kt +++ b/src/com/koxudaxi/pydantic/PydanticInspection.kt @@ -115,7 +115,7 @@ class PydanticInspection : PyInspection() { val pyClass = getPyClassByAttribute(node) ?: return if (!isPydanticModel(pyClass, true, myTypeEvalContext)) return if (node.annotation != null) return - if ((node.leftHandSideExpression as? PyTargetExpressionImpl)?.text?.startsWith("_") == true) return + if (!isValidFieldName((node.leftHandSideExpression as? PyTargetExpressionImpl)?.text)) return registerProblem(node, "Untyped fields disallowed", ProblemHighlightType.WARNING) diff --git a/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt b/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt index f1280042..6aaf389e 100644 --- a/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt +++ b/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt @@ -200,7 +200,7 @@ class PydanticTypeProvider : PyTypeProviderBase() { } argumentWithoutModelName .filter { it is PyKeywordArgument || (it as? PyStarArgumentImpl)?.isKeyword == true } - .filterNot { it.name?.startsWith("_") == true || it.name == "model_name" } + .filter { isValidFieldName(it.name) || it.name != "model_name" } .forEach { val parameter = fieldToParameter(it, context, hashMapOf(), typed)!! parameter.name?.let { name -> diff --git a/testData/completion/fieldCustomRoot.py b/testData/completion/fieldCustomRoot.py new file mode 100644 index 00000000..bb081f64 --- /dev/null +++ b/testData/completion/fieldCustomRoot.py @@ -0,0 +1,7 @@ +from builtins import * +from pydantic import BaseModel + +class A(BaseModel): + __root__: str + +A(). \ No newline at end of file diff --git a/testData/completion/keywordArgumentCustomRoot.py b/testData/completion/keywordArgumentCustomRoot.py new file mode 100644 index 00000000..1532fcd6 --- /dev/null +++ b/testData/completion/keywordArgumentCustomRoot.py @@ -0,0 +1,12 @@ +from builtins import * + +from pydantic import BaseModel + + +class A(BaseModel): + __root__: str + +class B(A): + hij: str + +A() \ No newline at end of file diff --git a/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt b/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt index 9e428996..d639d84b 100644 --- a/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt +++ b/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt @@ -33,6 +33,14 @@ open class PydanticCompletionTest : PydanticTestCase() { ) } + fun testKeywordArgumentCustomRoot() { + doFieldTest( + listOf( + Pair("__root__=", "str A"), + ) + ) + } + fun testKeywordArgumentDot() { doFieldTest( listOf(Pair("___slots__", "BaseModel")) @@ -445,6 +453,14 @@ open class PydanticCompletionTest : PydanticTestCase() { ) ) } + fun testFieldCustomRoot() { + doFieldTest( + listOf( + Pair("__root__", "str A"), + Pair("___slots__", "BaseModel") + ) + ) + } fun testFieldOptional() { doFieldTest( From 5e6f4d812dbe6154d9e7df01ba7eef09857c79b1 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 16 Dec 2020 19:04:42 +0900 Subject: [PATCH 2/4] Fix invalid code --- testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt b/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt index d639d84b..fe0d74ff 100644 --- a/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt +++ b/testSrc/com/koxudaxi/pydantic/PydanticCompletionTest.kt @@ -36,7 +36,7 @@ open class PydanticCompletionTest : PydanticTestCase() { fun testKeywordArgumentCustomRoot() { doFieldTest( listOf( - Pair("__root__=", "str A"), + Pair("__root__=", "str A") ) ) } From 5357db1acd7d89e5084083149d34ef3ee4fe4ebf Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Thu, 17 Dec 2020 11:55:45 +0900 Subject: [PATCH 3/4] add inspection of custom root --- .../koxudaxi/pydantic/PydanticInspection.kt | 11 +++++++ testData/inspection/customRoot.py | 29 +++++++++++++++++++ .../pydantic/PydanticInspectionTest.kt | 4 +++ 3 files changed, 44 insertions(+) create mode 100644 testData/inspection/customRoot.py diff --git a/src/com/koxudaxi/pydantic/PydanticInspection.kt b/src/com/koxudaxi/pydantic/PydanticInspection.kt index 76141bc7..bad1771e 100644 --- a/src/com/koxudaxi/pydantic/PydanticInspection.kt +++ b/src/com/koxudaxi/pydantic/PydanticInspection.kt @@ -63,6 +63,7 @@ class PydanticInspection : PyInspection() { if (pydanticConfigService.currentWarnUntypedFields) { inspectWarnUntypedFields(node) } + inspectCustomRootField(node) inspectReadOnlyProperty(node) } @@ -120,6 +121,16 @@ class PydanticInspection : PyInspection() { "Untyped fields disallowed", ProblemHighlightType.WARNING) } + + private fun inspectCustomRootField(node: PyAssignmentStatement) { + val pyClass = getPyClassByAttribute(node) ?: return + if (!isPydanticModel(pyClass, true, myTypeEvalContext)) return + val fieldName = (node.leftHandSideExpression as? PyTargetExpressionImpl)?.text ?: return + if (fieldName.startsWith('_')) return + pyClass.findClassAttribute("__root__", true, myTypeEvalContext) ?: return + registerProblem(node, + "__root__ cannot be mixed with other fields", ProblemHighlightType.WARNING) + } } // override fun createOptionsPanel(): JComponent? { diff --git a/testData/inspection/customRoot.py b/testData/inspection/customRoot.py new file mode 100644 index 00000000..f0f2dba3 --- /dev/null +++ b/testData/inspection/customRoot.py @@ -0,0 +1,29 @@ +from pydantic import BaseModel + + +class A(BaseModel): + __root__ = 'xyz' + + +class B(BaseModel): + a = 'xyz' + + +class C(BaseModel): + __root__ = 'xyz' + b = 'xyz' + + +class D(BaseModel): + __root__ = 'xyz' + _c = 'xyz' + __c = 'xyz' + +class E: + __root__ = 'xyz' + e = 'xyz' + +def f(): + __root__ = 'xyz' + g = 'xyz' + diff --git a/testSrc/com/koxudaxi/pydantic/PydanticInspectionTest.kt b/testSrc/com/koxudaxi/pydantic/PydanticInspectionTest.kt index 333ee687..eff6485e 100644 --- a/testSrc/com/koxudaxi/pydantic/PydanticInspectionTest.kt +++ b/testSrc/com/koxudaxi/pydantic/PydanticInspectionTest.kt @@ -59,4 +59,8 @@ open class PydanticInspectionTest : PydanticInspectionBase() { pydanticConfigService.warnUntypedFields = true doTest() } + + fun testCustomRoot() { + doTest() + } } From 891827638426f8da7928da42ae57b74a31feea3c Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Thu, 17 Dec 2020 12:52:12 +0900 Subject: [PATCH 4/4] update history --- resources/META-INF/plugin.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 9d49e4a3..a8ae3b57 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -1,9 +1,14 @@ com.koxudaxi.pydantic Pydantic - 0.1.17 + 0.1.18 Koudai Aono @koxudaxi version 0.1.18 +

Features

+
    +
  • Support custom root field [#227]
  • +

version 0.1.17

Features