diff --git a/CHANGELOG.md b/CHANGELOG.md index a5eef50a..d80787f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix wrong inspections when a model has a __call__ method [[#655](https://github.com/koxudaxi/pydantic-pycharm-plugin/pull/655)] - Reduce unnecessary resolve in type providers [[#656](https://github.com/koxudaxi/pydantic-pycharm-plugin/pull/656)] - Optimize resolving pydantic class [[#658](https://github.com/koxudaxi/pydantic-pycharm-plugin/pull/658)] +- Improve dynamic model field detection [[#659](https://github.com/koxudaxi/pydantic-pycharm-plugin/pull/659)] ## 0.3.17 - 2022-12-16 - Support Union operator [[#602](https://github.com/koxudaxi/pydantic-pycharm-plugin/pull/602)] diff --git a/src/com/koxudaxi/pydantic/Pydantic.kt b/src/com/koxudaxi/pydantic/Pydantic.kt index 9038b6d3..9ef8e112 100644 --- a/src/com/koxudaxi/pydantic/Pydantic.kt +++ b/src/com/koxudaxi/pydantic/Pydantic.kt @@ -371,7 +371,7 @@ fun getConfigValue(name: String, value: Any?, context: TypeEvalContext): Any? { ConfigType.LIST_PYTYPE -> { if (value is PyElement) { when (val tupleValue = PsiTreeUtil.findChildOfType(value, PyTupleExpression::class.java)) { - is PyTupleExpression -> tupleValue.toList().mapNotNull { getPyTypeFromPyExpression(it, context) } + is PyTupleExpression -> tupleValue.toList().mapNotNull { context.getType(it) } else -> null } } else null @@ -558,28 +558,14 @@ val PyCallableParameter.required: Boolean get() = !hasDefaultValue() || (defaultValue !is PyNoneLiteralExpression && defaultValueText == "...") -fun getPyTypeFromPyExpression(pyExpression: PyExpression, context: TypeEvalContext): PyType? { - return when (pyExpression) { - is PyType -> pyExpression - is PyReferenceExpression -> { - getResolvedPsiElements(pyExpression, context) - .asSequence() - .filterIsInstance() - .map { pyClass -> pyClass.getType(context)?.getReturnType(context) } - .firstOrNull() - } - else -> null - } -} - internal fun hasTargetPyType( pyExpression: PyExpression, targetPyTypes: List, context: TypeEvalContext, ): Boolean { val callee = (pyExpression as? PyCallExpression)?.callee ?: return false - val pyType = getPyTypeFromPyExpression(callee, context) ?: return false - val defaultValueTypeClassQName = pyType.declarationElement?.qualifiedName ?: return false + val pyType = callee.getType(context)?.pyClassTypes?.firstOrNull()?.getReturnType(context) + val defaultValueTypeClassQName = pyType?.declarationElement?.qualifiedName ?: return false return targetPyTypes.any { it.declarationElement?.qualifiedName == defaultValueTypeClassQName } } diff --git a/src/com/koxudaxi/pydantic/PydanticCompletionContributor.kt b/src/com/koxudaxi/pydantic/PydanticCompletionContributor.kt index 7e860959..090253f2 100644 --- a/src/com/koxudaxi/pydantic/PydanticCompletionContributor.kt +++ b/src/com/koxudaxi/pydantic/PydanticCompletionContributor.kt @@ -76,7 +76,7 @@ class PydanticCompletionContributor : CompletionContributor() { genericTypeMap: Map?, ): String? { - val parameter = typeProvider.fieldToParameter(pyTargetExpression, + val parameter = typeProvider.dynamicModelFieldToParameter(pyTargetExpression, ellipsis, typeEvalContext, pyClass, diff --git a/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt b/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt index 196cc2fb..d09476c1 100644 --- a/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt +++ b/src/com/koxudaxi/pydantic/PydanticTypeProvider.kt @@ -112,7 +112,7 @@ class PydanticTypeProvider : PyTypeProviderBase() { context: TypeEvalContext, pyClass: PyClass, pydanticVersion: KotlinVersion?, ): PyType? { - return fieldToParameter( + return dynamicModelFieldToParameter( pyTargetExpression, ellipsis, context, @@ -333,7 +333,7 @@ class PydanticTypeProvider : PyTypeProviderBase() { continue } baseClassCollected.putAll(getClassVariables(current, context) - .map { it to fieldToParameter(it, context, typed) } + .map { it to dynamicModelFieldToParameter(it, context, typed) } .mapNotNull { (field, parameter) -> parameter.name?.let { name -> Triple(field, parameter, name) } } @@ -359,7 +359,7 @@ class PydanticTypeProvider : PyTypeProviderBase() { .filter { (name, _) -> name.isValidFieldName && !name.startsWith('_') } .filter { (name, _) -> (newVersion || name != "model_name") } .map { (name, field) -> - val parameter = fieldToParameter(field, context, typed) + val parameter = dynamicModelFieldToParameter(field, context, typed) name to PydanticDynamicModel.createAttribute(name, parameter, field, context, false) } ) @@ -483,7 +483,7 @@ class PydanticTypeProvider : PyTypeProviderBase() { getClassVariables(current, context) .filterNot { isUntouchedClass(it.findAssignedValue(), config, context) } .mapNotNull { - fieldToParameter( + dynamicModelFieldToParameter( it, ellipsis, context, @@ -510,7 +510,7 @@ class PydanticTypeProvider : PyTypeProviderBase() { } - internal fun fieldToParameter( + internal fun dynamicModelFieldToParameter( field: PyTargetExpression, ellipsis: PyNoneLiteralExpression, context: TypeEvalContext, @@ -572,7 +572,7 @@ class PydanticTypeProvider : PyTypeProviderBase() { ) } - private fun fieldToParameter( + private fun dynamicModelFieldToParameter( field: PyExpression, context: TypeEvalContext, typed: Boolean = true, @@ -581,17 +581,14 @@ class PydanticTypeProvider : PyTypeProviderBase() { var defaultValue: PyExpression? = null when (val tupleValue = PsiTreeUtil.findChildOfType(field, PyTupleExpression::class.java)) { is PyTupleExpression -> { - tupleValue.toList().let { - if (it.size > 1) { - type = getPyTypeFromPyExpression(it[0], context) - defaultValue = it[1] + tupleValue.firstOrNull()?.let { + type = context.getType(it)?.pyClassTypes?.first()?.getReturnType(context) + defaultValue = it } - } } - else -> { type = context.getType(field) - defaultValue = (field as? PyKeywordArgumentImpl)?.valueExpression + defaultValue = (field as? PyKeywordArgument)?.valueExpression } } val typeForParameter = when {