PY-46623 Refactor PyKnownDecorator and related classes to be extendable

PyKnownDecorator is now a class instead of an enum, allowing extension to create new instances

GitOrigin-RevId: 846617f68e559f7f77af15a999f837391ac3090e
This commit is contained in:
evgeny.bovykin
2025-03-14 15:15:09 +00:00
committed by intellij-monorepo-bot
parent 3df8d77318
commit bf6e4735b6
17 changed files with 371 additions and 201 deletions
@@ -1,13 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.psi;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.Nullable;
public interface PyKnownDecoratorProvider {
ExtensionPointName<PyKnownDecoratorProvider> EP_NAME = ExtensionPointName.create("Pythonid.knownDecoratorProvider");
@Nullable
String toKnownDecorator(String decoratorName);
}
@@ -539,6 +539,7 @@
<inspectionExtension implementation="com.jetbrains.python.pyi.PyiInspectionExtension"/>
<inspectionExtension implementation="com.jetbrains.python.codeInsight.typing.PyTypingInspectionExtension"/>
<customPackageIdentifier implementation="com.jetbrains.python.pyi.PyiCustomPackageIdentifier"/>
<knownDecoratorProvider implementation="com.jetbrains.python.psi.PyStdKnownDecoratorProvider"/>
<!-- IPython -->
<pyReferenceResolveProvider implementation="com.jetbrains.python.psi.resolve.PyIPythonBuiltinReferenceResolveProvider"/>
@@ -12,7 +12,7 @@ import com.jetbrains.python.codeInsight.PyDataclassParameters.Type
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
import com.jetbrains.python.psi.*
import com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator
import com.jetbrains.python.psi.PyKnownDecorator
import com.jetbrains.python.psi.impl.PyCallExpressionHelper
import com.jetbrains.python.psi.impl.PyEvaluator
import com.jetbrains.python.psi.impl.StubAwareComputation
@@ -120,17 +120,17 @@ object PyDataclassNames {
* determine what settings dataclass has.
*/
private val DECORATOR_AND_TYPE_AND_PARAMETERS = listOf(
Triple(KnownDecorator.DATACLASSES_DATACLASS, PyDataclassParameters.PredefinedType.STD, PyDataclassNames.Dataclasses.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTR_S, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTR_ATTRS, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTR_ATTRIBUTES, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTR_DATACLASS, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTR_DEFINE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTR_MUTABLE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTR_FROZEN, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTRS_DEFINE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTRS_MUTABLE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(KnownDecorator.ATTRS_FROZEN, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.DATACLASSES_DATACLASS, PyDataclassParameters.PredefinedType.STD, PyDataclassNames.Dataclasses.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTR_S, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTR_ATTRS, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTR_ATTRIBUTES, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTR_DATACLASS, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTR_DEFINE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTR_MUTABLE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTR_FROZEN, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTRS_DEFINE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTRS_MUTABLE, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
Triple(PyKnownDecorator.ATTRS_FROZEN, PyDataclassParameters.PredefinedType.ATTRS, PyDataclassNames.Attrs.DECORATOR_PARAMETERS),
)
fun parseStdDataclassParameters(cls: PyClass, context: TypeEvalContext): PyDataclassParameters? {
@@ -521,7 +521,7 @@ private fun resolveDataclassParameters(
PyDataclassParameters.PredefinedType.ATTRS -> {
// TODO remove this hack, make it a proper field
val extraArguments = mutableMapOf<String, PyExpression>()
if (type.asPredefinedType == PyDataclassParameters.PredefinedType.ATTRS && stub.decoratorName() == KnownDecorator.ATTR_DATACLASS.qualifiedName) {
if (type.asPredefinedType == PyDataclassParameters.PredefinedType.ATTRS && stub.decoratorName() == PyKnownDecorator.ATTR_DATACLASS.qualifiedName) {
extraArguments["auto_attribs"] =
PyElementGenerator.getInstance(pyClass.project).createExpressionFromText(LanguageLevel.forElement(pyClass), PyNames.TRUE)
}
@@ -5,6 +5,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.util.QualifiedName
import com.intellij.util.containers.ContainerUtil
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
import com.jetbrains.python.psi.PyKnownDecorator
import com.jetbrains.python.psi.PyCallable
import com.jetbrains.python.psi.PyFunction
import com.jetbrains.python.psi.PyKnownDecoratorUtil
@@ -35,7 +36,7 @@ class PyFunctoolsWrapsDecoratedFunctionTypeProvider : PyTypeProviderBase() {
private fun resolveWrapped(function: PyFunction, context: TypeEvalContext): List<PsiElement> {
val decorator = function.decoratorList?.decorators?.find {
val qName = it.qualifiedName
qName != null && PyKnownDecoratorUtil.asKnownDecorators(qName).contains(PyKnownDecoratorUtil.KnownDecorator.FUNCTOOLS_WRAPS)
qName != null && PyKnownDecoratorUtil.asKnownDecorators(qName).contains(PyKnownDecorator.FUNCTOOLS_WRAPS)
} ?: return emptyList()
return StubAwareComputation.on(decorator)
.withCustomStub { it.getCustomStub(PyFunctoolsWrapsDecoratorStub::class.java) }
@@ -11,7 +11,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator;
import com.jetbrains.python.psi.PyKnownDecorator;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyTypeProvider;
import com.jetbrains.python.psi.impl.stubs.PyEnumAttributeStubType;
@@ -301,7 +301,7 @@ public final class PyStdlibTypeProvider extends PyTypeProviderBase {
}
private static boolean isEnumMember(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) {
return PyKnownDecoratorUtil.getKnownDecorators(decoratable, context).contains(KnownDecorator.ENUM_MEMBER);
return PyKnownDecoratorUtil.getKnownDecorators(decoratable, context).contains(PyKnownDecorator.ENUM_MEMBER);
}
private static @Nullable PyType getEnumAutoConstructorType(@NotNull PsiElement target,
@@ -50,8 +50,8 @@ import java.util.regex.Pattern;
import java.util.stream.Stream;
import static com.intellij.openapi.util.RecursionManager.doPreventingRecursion;
import static com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.TYPING_FINAL;
import static com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.TYPING_FINAL_EXT;
import static com.jetbrains.python.psi.PyKnownDecorator.TYPING_FINAL;
import static com.jetbrains.python.psi.PyKnownDecorator.TYPING_FINAL_EXT;
import static com.jetbrains.python.psi.PyUtil.as;
public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<PyTypingTypeProvider.Context> {
@@ -187,7 +187,7 @@ public static final String CONTEXT_MANAGER = "contextlib.AbstractContextManager"
* some synthetic values.
*/
public static final ImmutableSet<String> OPAQUE_NAMES = ImmutableSet.<String>builder()
.add(PyKnownDecoratorUtil.KnownDecorator.TYPING_OVERLOAD.name())
.add(PyKnownDecorator.TYPING_OVERLOAD.getQualifiedName().toString())
.add(ANY)
.add(TYPE_VAR)
.add(TYPE_VAR_EXT)
@@ -5,6 +5,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import com.jetbrains.python.PyPsiBundle
import com.jetbrains.python.psi.PyKnownDecorator
import com.jetbrains.python.psi.PyFunction
import com.jetbrains.python.psi.PyKnownDecoratorUtil
import com.jetbrains.python.psi.search.PySuperMethodsSearch
@@ -23,8 +24,8 @@ class PyOverridesInspection : PyInspection() {
super.visitPyFunction(node)
if (!PyKnownDecoratorUtil.getKnownDecorators(node, myTypeEvalContext)
.any { it == PyKnownDecoratorUtil.KnownDecorator.TYPING_OVERRIDE ||
it == PyKnownDecoratorUtil.KnownDecorator.TYPING_EXTENSIONS_OVERRIDE }) {
.any { it == PyKnownDecorator.TYPING_OVERRIDE ||
it == PyKnownDecorator.TYPING_EXTENSIONS_OVERRIDE }) {
return
}
@@ -12,7 +12,7 @@ import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
import com.jetbrains.python.codeInsight.typing.inspectProtocolSubclass
import com.jetbrains.python.codeInsight.typing.isProtocol
import com.jetbrains.python.psi.*
import com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.*
import com.jetbrains.python.psi.PyKnownDecorator.*
import com.jetbrains.python.psi.resolve.PyResolveContext
import com.jetbrains.python.psi.resolve.PyResolveUtil
import com.jetbrains.python.psi.resolve.RatedResolveResult
@@ -0,0 +1,182 @@
package com.jetbrains.python.psi;
import com.intellij.psi.util.QualifiedName;
import com.jetbrains.python.PyNames;
import org.jetbrains.annotations.NotNull;
// TODO provide more information about these decorators: attributes (e.g. lru_cache(f).cache_info), side-effects etc.
@SuppressWarnings("SpellCheckingInspection")
public class PyKnownDecorator {
private final QualifiedName myQualifiedName;
private final boolean myIsProperty;
private final boolean myIsMutableProperty;
private final boolean myIsGeneratorBasedCoroutine;
private final boolean myIsAbstract;
private final boolean myIsClassMethod;
private final boolean myIsStaticMethod;
public PyKnownDecorator(@NotNull String qualifiedName) {
myQualifiedName = QualifiedName.fromDottedString(qualifiedName);
myIsProperty = false;
myIsMutableProperty = false;
myIsGeneratorBasedCoroutine = false;
myIsAbstract = false;
myIsClassMethod = false;
myIsStaticMethod = false;
}
private PyKnownDecorator(@NotNull String qualifiedName, boolean isProperty, boolean isMutableProperty, boolean isGeneratorBasedCoroutine, boolean isAbstract,
boolean isClassMethod,
boolean isStaticMethod) {
myQualifiedName = QualifiedName.fromDottedString(qualifiedName);
myIsProperty = isProperty;
myIsMutableProperty = isMutableProperty;
myIsGeneratorBasedCoroutine = isGeneratorBasedCoroutine;
myIsAbstract = isAbstract;
myIsClassMethod = isClassMethod;
myIsStaticMethod = isStaticMethod;
}
public static class Builder {
private boolean isProperty = false;
private boolean isMutableProperty = false;
private boolean isGeneratorBasedCoroutine = false;
private boolean isAbstract = false;
private boolean isClassMethod = false;
private boolean isStaticMethod = false;
private final String qualifiedName;
public Builder(String name) {
qualifiedName = name;
}
public @NotNull Builder setProperty() {
isProperty = true;
return this;
}
public @NotNull Builder setMutableProperty() {
isMutableProperty = true;
return this;
}
public @NotNull Builder setGeneratorBasedCoroutine() {
isGeneratorBasedCoroutine = true;
return this;
}
public @NotNull Builder setAbstract() {
isAbstract = true;
return this;
}
public @NotNull Builder setClassMethod() {
isClassMethod = true;
return this;
}
public @NotNull Builder setStaticMethod() {
isStaticMethod = true;
return this;
}
public @NotNull PyKnownDecorator build() {
return new PyKnownDecorator(qualifiedName, isProperty, isMutableProperty, isGeneratorBasedCoroutine, isAbstract, isClassMethod, isStaticMethod);
}
}
public @NotNull QualifiedName getQualifiedName() {
return myQualifiedName;
}
public @NotNull String getShortName() {
//noinspection ConstantConditions
return myQualifiedName.getLastComponent();
}
public final static PyKnownDecorator STATICMETHOD = new PyKnownDecorator.Builder(PyNames.STATICMETHOD).setStaticMethod().build();
public final static PyKnownDecorator CLASSMETHOD = new PyKnownDecorator.Builder(PyNames.CLASSMETHOD).setClassMethod().build();
public final static PyKnownDecorator PROPERTY = new PyKnownDecorator.Builder(PyNames.PROPERTY).setProperty().build();
public final static PyKnownDecorator FUNCTOOLS_LRU_CACHE = new PyKnownDecorator("functools.lru_cache");
public final static PyKnownDecorator FUNCTOOLS_WRAPS = new PyKnownDecorator("functools.wraps");
public final static PyKnownDecorator FUNCTOOLS_TOTAL_ORDERING = new PyKnownDecorator("functools.total_ordering");
public final static PyKnownDecorator FUNCTOOLS_SINGLEDISPATCH = new PyKnownDecorator("functools.singledispatch");
public final static PyKnownDecorator FUNCTOOLS_CACHED_PROPERTY = new PyKnownDecorator.Builder("functools.cached_property").setProperty().setMutableProperty().build();
public final static PyKnownDecorator ABC_ABSTRACTMETHOD = new PyKnownDecorator.Builder("abc.abstractmethod").setAbstract().build();
public final static PyKnownDecorator ABC_ABSTRACTCLASSMETHOD = new PyKnownDecorator.Builder("abc.abstractclassmethod").setAbstract().setClassMethod().build();
public final static PyKnownDecorator ABC_ABSTRACTSTATICMETHOD = new PyKnownDecorator.Builder("abc.abstractstaticmethod").setAbstract().setStaticMethod().build();
public final static PyKnownDecorator ABC_ABSTRACTPROPERTY = new PyKnownDecorator.Builder("abc.abstractproperty").setAbstract().setProperty().build();
public final static PyKnownDecorator ASYNCIO_TASKS_COROUTINE = new PyKnownDecorator.Builder("asyncio.tasks.coroutine").setGeneratorBasedCoroutine().build();
public final static PyKnownDecorator ASYNCIO_COROUTINES_COROUTINE = new PyKnownDecorator.Builder("asyncio.coroutines.coroutine").setGeneratorBasedCoroutine().build();
public final static PyKnownDecorator TYPES_COROUTINE = new PyKnownDecorator.Builder("types.coroutine").setGeneratorBasedCoroutine().build();
public final static PyKnownDecorator UNITTEST_SKIP = new PyKnownDecorator("unittest.case.skip");
public final static PyKnownDecorator UNITTEST_SKIP_IF = new PyKnownDecorator("unittest.case.skipIf");
public final static PyKnownDecorator UNITTEST_SKIP_UNLESS = new PyKnownDecorator("unittest.case.skipUnless");
public final static PyKnownDecorator UNITTEST_EXPECTED_FAILURE = new PyKnownDecorator("unittest.case.expectedFailure");
public final static PyKnownDecorator UNITTEST_MOCK_PATCH = new PyKnownDecorator("unittest.mock.patch");
public final static PyKnownDecorator TYPING_OVERLOAD = new PyKnownDecorator("typing." + PyNames.OVERLOAD);
public final static PyKnownDecorator TYPING_OVERRIDE = new PyKnownDecorator("typing." + PyNames.OVERRIDE);
public final static PyKnownDecorator TYPING_EXTENSIONS_OVERRIDE = new PyKnownDecorator("typing_extensions." + PyNames.OVERRIDE);
public final static PyKnownDecorator TYPING_RUNTIME = new PyKnownDecorator("typing.runtime");
public final static PyKnownDecorator TYPING_RUNTIME_EXT = new PyKnownDecorator("typing_extensions.runtime");
public final static PyKnownDecorator TYPING_RUNTIME_CHECKABLE = new PyKnownDecorator("typing.runtime_checkable");
public final static PyKnownDecorator TYPING_RUNTIME_CHECKABLE_EXT = new PyKnownDecorator("typing_extensions.runtime_checkable");
public final static PyKnownDecorator TYPING_FINAL = new PyKnownDecorator("typing.final");
public final static PyKnownDecorator TYPING_FINAL_EXT = new PyKnownDecorator("typing_extensions.final");
public final static PyKnownDecorator TYPING_DEPRECATED = new PyKnownDecorator("typing_extensions.deprecated");
public final static PyKnownDecorator WARNING_DEPRECATED = new PyKnownDecorator("warnings.deprecated");
public final static PyKnownDecorator REPRLIB_RECURSIVE_REPR = new PyKnownDecorator("reprlib.recursive_repr");
public final static PyKnownDecorator PYRAMID_DECORATOR_REIFY = new PyKnownDecorator.Builder("pyramid.decorator.reify").setProperty().build();
public final static PyKnownDecorator KOMBU_UTILS_CACHED_PROPERTY = new PyKnownDecorator.Builder("kombu.utils.cached_property").setProperty().setMutableProperty().build();
public final static PyKnownDecorator DATACLASSES_DATACLASS = new PyKnownDecorator("dataclasses.dataclass");
public final static PyKnownDecorator ATTR_S = new PyKnownDecorator("attr.s");
public final static PyKnownDecorator ATTR_ATTRS = new PyKnownDecorator("attr.attrs");
public final static PyKnownDecorator ATTR_ATTRIBUTES = new PyKnownDecorator("attr.attributes");
public final static PyKnownDecorator ATTR_DATACLASS = new PyKnownDecorator("attr.dataclass");
public final static PyKnownDecorator ATTR_DEFINE = new PyKnownDecorator("attr.define");
public final static PyKnownDecorator ATTR_MUTABLE = new PyKnownDecorator("attr.mutable");
public final static PyKnownDecorator ATTR_FROZEN = new PyKnownDecorator("attr.frozen");
public final static PyKnownDecorator ATTRS_DEFINE = new PyKnownDecorator("attrs.define");
public final static PyKnownDecorator ATTRS_MUTABLE = new PyKnownDecorator("attrs.mutable");
public final static PyKnownDecorator ATTRS_FROZEN = new PyKnownDecorator("attrs.frozen");
public final static PyKnownDecorator PYTEST_FIXTURES_FIXTURE = new PyKnownDecorator("_pytest.fixtures.fixture");
public final static PyKnownDecorator ENUM_MEMBER = new PyKnownDecorator(PyNames.TYPE_ENUM_MEMBER);
public final static PyKnownDecorator ENUM_NONMEMBER = new PyKnownDecorator(PyNames.TYPE_ENUM_NONMEMBER);
/**
* Mutable properties support __set__ and __delete__
*/
public boolean isMutableProperty() {
return myIsMutableProperty;
}
public boolean isProperty() {
return myIsProperty;
}
public boolean isGeneratorBasedCoroutine() {
return myIsGeneratorBasedCoroutine;
}
public boolean isAbstract() {
return myIsAbstract;
}
public boolean isClassMethod() {
return myIsClassMethod;
}
public boolean isStaticMethod() {
return myIsStaticMethod;
}
}
@@ -0,0 +1,26 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.psi;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
public interface PyKnownDecoratorProvider {
ExtensionPointName<PyKnownDecoratorProvider> EP_NAME = ExtensionPointName.create("Pythonid.knownDecoratorProvider");
default @NotNull Collection<PyKnownDecorator> getKnownDecorators() {
return Collections.emptyList();
}
/**
* @deprecated Use {@link #getKnownDecorators()} instead to provide a new {@link PyKnownDecorator} using {@link PyKnownDecorator.Builder}
*/
@Deprecated(forRemoval = true)
@Nullable default String toKnownDecorator(String decoratorName) {
return null;
}
}
@@ -6,7 +6,6 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.FunctionParameter;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.PyResolveUtil;
@@ -15,10 +14,12 @@ import com.jetbrains.python.pyi.PyiFile;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import static com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.*;
import static com.jetbrains.python.psi.PyKnownDecorator.*;
/**
* Contains list of well-behaved decorators from Pythons standard library, that don't change
@@ -31,120 +32,15 @@ public final class PyKnownDecoratorUtil {
private PyKnownDecoratorUtil() {
}
// TODO provide more information about these decorators: attributes (e.g. lru_cache(f).cache_info), side-effects etc.
@SuppressWarnings("SpellCheckingInspection")
public enum KnownDecorator {
STATICMETHOD(PyNames.STATICMETHOD),
CLASSMETHOD(PyNames.CLASSMETHOD),
PROPERTY(PyNames.PROPERTY),
FUNCTOOLS_LRU_CACHE("functools.lru_cache"),
FUNCTOOLS_WRAPS("functools.wraps"),
FUNCTOOLS_TOTAL_ORDERING("functools.total_ordering"),
FUNCTOOLS_SINGLEDISPATCH("functools.singledispatch"),
FUNCTOOLS_CACHED_PROPERTY("functools.cached_property"),
ABC_ABSTRACTMETHOD("abc.abstractmethod"),
ABC_ABSTRACTCLASSMETHOD("abc.abstractclassmethod"),
ABC_ABSTRACTSTATICMETHOD("abc.abstractstaticmethod"),
ABC_ABSTRACTPROPERTY("abc.abstractproperty"),
//ATEXIT_REGISTER("atexit.register", true),
//ATEXIT_UNREGISTER("atexit.unregister", false),
ASYNCIO_TASKS_COROUTINE("asyncio.tasks.coroutine"),
ASYNCIO_COROUTINES_COROUTINE("asyncio.coroutines.coroutine"),
TYPES_COROUTINE("types.coroutine"),
UNITTEST_SKIP("unittest.case.skip"),
UNITTEST_SKIP_IF("unittest.case.skipIf"),
UNITTEST_SKIP_UNLESS("unittest.case.skipUnless"),
UNITTEST_EXPECTED_FAILURE("unittest.case.expectedFailure"),
UNITTEST_MOCK_PATCH("unittest.mock.patch"),
TYPING_OVERLOAD("typing." + PyNames.OVERLOAD),
TYPING_OVERRIDE("typing." + PyNames.OVERRIDE),
TYPING_EXTENSIONS_OVERRIDE("typing_extensions." + PyNames.OVERRIDE),
TYPING_RUNTIME("typing.runtime"),
TYPING_RUNTIME_EXT("typing_extensions.runtime"),
TYPING_RUNTIME_CHECKABLE("typing.runtime_checkable"),
TYPING_RUNTIME_CHECKABLE_EXT("typing_extensions.runtime_checkable"),
TYPING_FINAL("typing.final"),
TYPING_FINAL_EXT("typing_extensions.final"),
TYPING_DEPRECATED("typing_extensions.deprecated"),
WARNING_DEPRECATED("warnings.deprecated"),
REPRLIB_RECURSIVE_REPR("reprlib.recursive_repr"),
PYRAMID_DECORATOR_REIFY("pyramid.decorator.reify"),
DJANGO_UTILS_FUNCTIONAL_CACHED_PROPERTY("django.utils.functional.cached_property"),
KOMBU_UTILS_CACHED_PROPERTY("kombu.utils.cached_property"),
DATACLASSES_DATACLASS("dataclasses.dataclass"),
ATTR_S("attr.s"),
ATTR_ATTRS("attr.attrs"),
ATTR_ATTRIBUTES("attr.attributes"),
ATTR_DATACLASS("attr.dataclass"),
ATTR_DEFINE("attr.define"),
ATTR_MUTABLE("attr.mutable"),
ATTR_FROZEN("attr.frozen"),
ATTRS_DEFINE("attrs.define"),
ATTRS_MUTABLE("attrs.mutable"),
ATTRS_FROZEN("attrs.frozen"),
PYTEST_FIXTURES_FIXTURE("_pytest.fixtures.fixture"),
ENUM_MEMBER(PyNames.TYPE_ENUM_MEMBER),
ENUM_NONMEMBER(PyNames.TYPE_ENUM_NONMEMBER);
private final QualifiedName myQualifiedName;
KnownDecorator(@NotNull String qualifiedName) {
myQualifiedName = QualifiedName.fromDottedString(qualifiedName);
}
public @NotNull QualifiedName getQualifiedName() {
return myQualifiedName;
}
public @NotNull String getShortName() {
//noinspection ConstantConditions
return myQualifiedName.getLastComponent();
}
}
private static final Set<KnownDecorator> ABSTRACT_DECORATORS = EnumSet.of(ABC_ABSTRACTMETHOD,
ABC_ABSTRACTPROPERTY,
ABC_ABSTRACTSTATICMETHOD,
ABC_ABSTRACTCLASSMETHOD);
private static final Set<KnownDecorator> PROPERTY_DECORATORS = EnumSet.of(PROPERTY,
ABC_ABSTRACTPROPERTY,
PYRAMID_DECORATOR_REIFY,
FUNCTOOLS_CACHED_PROPERTY,
DJANGO_UTILS_FUNCTIONAL_CACHED_PROPERTY,
KOMBU_UTILS_CACHED_PROPERTY);
private static final Set<KnownDecorator> GENERATOR_BASED_COROUTINE_DECORATORS = EnumSet.of(ASYNCIO_TASKS_COROUTINE,
ASYNCIO_COROUTINES_COROUTINE,
TYPES_COROUTINE);
private static final Map<QualifiedName, KnownDecorator> BY_QUALIFIED_NAME =
StreamEx.of(values()).toMap(KnownDecorator::getQualifiedName, x -> x);
private static final Map<String, List<KnownDecorator>> BY_SHORT_NAME =
StreamEx.of(values()).groupingBy(KnownDecorator::getShortName);
/**
* Map decorators of element to {@link PyKnownDecoratorUtil.KnownDecorator}.
* Map decorators of element to {@link PyKnownDecorator}.
*
* @param element decoratable element to check
* @param context type evaluation context. If it doesn't allow switch to AST, decorators will be compared by the text of the last component
* of theirs qualified names.
* @return list of known decorators in declaration order with duplicates (with any)
*/
public static @NotNull List<KnownDecorator> getKnownDecorators(@NotNull PyDecoratable element, @NotNull TypeEvalContext context) {
public static @NotNull List<PyKnownDecorator> getKnownDecorators(@NotNull PyDecoratable element, @NotNull TypeEvalContext context) {
final PyDecoratorList decoratorList = element.getDecoratorList();
if (decoratorList == null) {
return Collections.emptyList();
@@ -155,7 +51,7 @@ public final class PyKnownDecoratorUtil {
.toImmutableList();
}
public static @NotNull List<KnownDecorator> asKnownDecorators(@NotNull PyDecorator decorator, @NotNull TypeEvalContext context) {
public static @NotNull List<PyKnownDecorator> asKnownDecorators(@NotNull PyDecorator decorator, @NotNull TypeEvalContext context) {
final QualifiedName qualifiedName = decorator.getQualifiedName();
if (qualifiedName == null) {
return Collections.emptyList();
@@ -175,7 +71,7 @@ public final class PyKnownDecoratorUtil {
.map(PyQualifiedNameOwner::getQualifiedName)
.nonNull()
.map(QualifiedName::fromDottedString)
.map(BY_QUALIFIED_NAME::get)
.map(PyKnownDecoratorUtil::findByQualifiedName)
.nonNull()
.toImmutableList();
}
@@ -185,10 +81,14 @@ public final class PyKnownDecoratorUtil {
}
@ApiStatus.Internal
public static @NotNull List<KnownDecorator> asKnownDecorators(@NotNull QualifiedName qualifiedName) {
public static @NotNull List<PyKnownDecorator> asKnownDecorators(@NotNull QualifiedName qualifiedName) {
// The method might have been called during building of PSI stub indexes. Thus, we can't leave this file's boundaries.
// TODO Use proper local resolve to imported names here
return Collections.unmodifiableList(BY_SHORT_NAME.getOrDefault(qualifiedName.getLastComponent(), Collections.emptyList()));
String lastComponent = qualifiedName.getLastComponent();
if (lastComponent == null) {
return Collections.emptyList();
}
return findByShortName(lastComponent);
}
/**
@@ -197,7 +97,7 @@ public final class PyKnownDecoratorUtil {
* @param element decoratable element to check
* @param context type evaluation context. If it doesn't allow switch to AST, decorators will be compared by the text of the last component
* of theirs qualified names.
* @see PyKnownDecoratorUtil.KnownDecorator
* @see PyKnownDecorator
*/
public static boolean hasUnknownDecorator(@NotNull PyDecoratable element, @NotNull TypeEvalContext context) {
return !allDecoratorsAreKnown(element, getKnownDecorators(element, context));
@@ -209,18 +109,14 @@ public final class PyKnownDecoratorUtil {
* @param element Python function to check
* @param context type evaluation context. If it doesn't allow switch to AST, decorators will be compared by the text of the last component
* of theirs qualified names.
* @see PyKnownDecoratorUtil.KnownDecorator
* @see PyKnownDecorator
*/
public static boolean hasAbstractDecorator(@NotNull PyDecoratable element, @NotNull TypeEvalContext context) {
return ContainerUtil.exists(getKnownDecorators(element, context), ABSTRACT_DECORATORS::contains);
}
public static boolean isPropertyDecorator(@NotNull PyDecorator decorator, @NotNull TypeEvalContext context) {
return ContainerUtil.exists(asKnownDecorators(decorator, context), PROPERTY_DECORATORS::contains);
return ContainerUtil.exists(getKnownDecorators(element, context), knownDecorator -> knownDecorator.isAbstract());
}
public static boolean hasGeneratorBasedCoroutineDecorator(@NotNull PyFunction function, @NotNull TypeEvalContext context) {
return ContainerUtil.exists(getKnownDecorators(function, context), GENERATOR_BASED_COROUTINE_DECORATORS::contains);
return ContainerUtil.exists(getKnownDecorators(function, context), knownDecorator -> knownDecorator.isGeneratorBasedCoroutine());
}
public static boolean isResolvedToGeneratorBasedCoroutine(@NotNull PyCallExpression receiver,
@@ -237,12 +133,12 @@ public final class PyKnownDecoratorUtil {
}
public static boolean hasUnknownOrChangingSignatureDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) {
final List<KnownDecorator> decorators = getKnownDecorators(decoratable, context);
final List<PyKnownDecorator> decorators = getKnownDecorators(decoratable, context);
return !allDecoratorsAreKnown(decoratable, decorators) || decorators.contains(UNITTEST_MOCK_PATCH);
}
public static boolean hasUnknownOrChangingReturnTypeDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) {
final List<KnownDecorator> decorators = getKnownDecorators(decoratable, context);
final List<PyKnownDecorator> decorators = getKnownDecorators(decoratable, context);
if (!allDecoratorsAreKnown(decoratable, decorators)) {
return true;
@@ -252,12 +148,12 @@ public final class PyKnownDecoratorUtil {
}
public static boolean hasChangingReturnTypeDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) {
final List<KnownDecorator> decorators = getKnownDecorators(decoratable, context);
final List<PyKnownDecorator> decorators = getKnownDecorators(decoratable, context);
return ContainerUtil.exists(decorators, d -> d == UNITTEST_MOCK_PATCH);
}
public static boolean hasUnknownOrUpdatingAttributesDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) {
final List<KnownDecorator> decorators = getKnownDecorators(decoratable, context);
final List<PyKnownDecorator> decorators = getKnownDecorators(decoratable, context);
if (!allDecoratorsAreKnown(decoratable, decorators)) {
return true;
@@ -271,11 +167,37 @@ public final class PyKnownDecoratorUtil {
);
}
private static boolean allDecoratorsAreKnown(@NotNull PyDecoratable element, @NotNull List<KnownDecorator> decorators) {
private static boolean allDecoratorsAreKnown(@NotNull PyDecoratable element, @NotNull List<PyKnownDecorator> decorators) {
final PyDecoratorList decoratorList = element.getDecoratorList();
return decoratorList == null
? decorators.isEmpty()
: decoratorList.getDecorators().length == StreamEx.of(decorators).groupingBy(KnownDecorator::getShortName).size();
: decoratorList.getDecorators().length == StreamEx.of(decorators).groupingBy(PyKnownDecorator::getShortName).size();
}
private static List<PyKnownDecorator> findByShortName(@NotNull String shortName) {
return PyKnownDecoratorProvider.EP_NAME.getExtensionList().stream()
.flatMap(knownDecoratorProvider -> {
Collection<PyKnownDecorator> decorators = knownDecoratorProvider.getKnownDecorators();
if (!decorators.isEmpty()) {
return decorators.stream();
}
// Fallback to the old implementation that will be removed in the future release
String knownDecorator = knownDecoratorProvider.toKnownDecorator(shortName);
if (knownDecorator != null && !knownDecorator.isEmpty() && !knownDecorator.equals(shortName)) {
return StreamEx.of(findByShortName(knownDecorator));
}
return StreamEx.empty();
})
.filter(knownDecorator -> knownDecorator.getShortName().equals(shortName))
.toList();
}
private static @Nullable PyKnownDecorator findByQualifiedName(@NotNull QualifiedName qualifiedName) {
return PyKnownDecoratorProvider.EP_NAME.getExtensionList().stream()
.flatMap(knownDecoratorProvider -> knownDecoratorProvider.getKnownDecorators().stream())
.filter(knownDecorator -> knownDecorator.getQualifiedName().equals(qualifiedName))
.findFirst()
.orElse(null);
}
public enum FunctoolsWrapsParameters implements FunctionParameter {
@@ -0,0 +1,56 @@
package com.jetbrains.python.psi
class PyStdKnownDecoratorProvider : PyKnownDecoratorProvider {
override fun getKnownDecorators(): Collection<PyKnownDecorator> {
return listOf(
PyKnownDecorator.STATICMETHOD,
PyKnownDecorator.CLASSMETHOD,
PyKnownDecorator.PROPERTY,
PyKnownDecorator.FUNCTOOLS_LRU_CACHE,
PyKnownDecorator.FUNCTOOLS_WRAPS,
PyKnownDecorator.FUNCTOOLS_TOTAL_ORDERING,
PyKnownDecorator.FUNCTOOLS_SINGLEDISPATCH,
PyKnownDecorator.FUNCTOOLS_CACHED_PROPERTY,
PyKnownDecorator.ABC_ABSTRACTMETHOD,
PyKnownDecorator.ABC_ABSTRACTCLASSMETHOD,
PyKnownDecorator.ABC_ABSTRACTSTATICMETHOD,
PyKnownDecorator.ABC_ABSTRACTPROPERTY,
PyKnownDecorator.ASYNCIO_TASKS_COROUTINE,
PyKnownDecorator.ASYNCIO_COROUTINES_COROUTINE,
PyKnownDecorator.TYPES_COROUTINE,
PyKnownDecorator.UNITTEST_SKIP,
PyKnownDecorator.UNITTEST_SKIP_IF,
PyKnownDecorator.UNITTEST_SKIP_UNLESS,
PyKnownDecorator.UNITTEST_EXPECTED_FAILURE,
PyKnownDecorator.UNITTEST_MOCK_PATCH,
PyKnownDecorator.TYPING_OVERLOAD,
PyKnownDecorator.TYPING_OVERRIDE,
PyKnownDecorator.TYPING_EXTENSIONS_OVERRIDE,
PyKnownDecorator.TYPING_RUNTIME,
PyKnownDecorator.TYPING_RUNTIME_EXT,
PyKnownDecorator.TYPING_RUNTIME_CHECKABLE,
PyKnownDecorator.TYPING_RUNTIME_CHECKABLE_EXT,
PyKnownDecorator.TYPING_FINAL,
PyKnownDecorator.TYPING_FINAL_EXT,
PyKnownDecorator.TYPING_DEPRECATED,
PyKnownDecorator.WARNING_DEPRECATED,
PyKnownDecorator.REPRLIB_RECURSIVE_REPR,
PyKnownDecorator.PYRAMID_DECORATOR_REIFY,
PyKnownDecorator.KOMBU_UTILS_CACHED_PROPERTY,
PyKnownDecorator.DATACLASSES_DATACLASS,
PyKnownDecorator.ATTR_S,
PyKnownDecorator.ATTR_ATTRS,
PyKnownDecorator.ATTR_ATTRIBUTES,
PyKnownDecorator.ATTR_DATACLASS,
PyKnownDecorator.ATTR_DEFINE,
PyKnownDecorator.ATTR_MUTABLE,
PyKnownDecorator.ATTR_FROZEN,
PyKnownDecorator.ATTRS_DEFINE,
PyKnownDecorator.ATTRS_MUTABLE,
PyKnownDecorator.ATTRS_FROZEN,
PyKnownDecorator.PYTEST_FIXTURES_FIXTURE,
PyKnownDecorator.ENUM_MEMBER,
PyKnownDecorator.ENUM_NONMEMBER
)
}
}
@@ -674,24 +674,25 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
final PyDecoratorList decoratorList = method.getDecoratorList();
if (decoratorList != null) {
for (PyDecorator deco : decoratorList.getDecorators()) {
final QualifiedName qname = deco.getQualifiedName();
if (qname != null) {
String decoName = qname.toString();
for (PyKnownDecoratorProvider provider : PyKnownDecoratorProvider.EP_NAME.getExtensionList()) {
final String knownName = provider.toKnownDecorator(decoName);
if (knownName != null) {
decoName = knownName;
}
}
if (PyNames.CACHED_PROPERTY.equals(decoName)) {
TypeEvalContext context = TypeEvalContext.codeInsightFallback(getProject());
List<PyKnownDecorator> knownDecorators = PyKnownDecoratorUtil.asKnownDecorators(deco, context);
for (PyKnownDecorator knownDecorator : knownDecorators) {
if (knownDecorator.isMutableProperty()) {
getter = new Maybe<>(method);
setter = new Maybe<>(method);
deleter = new Maybe<>(method);
break;
}
if (PyNames.PROPERTY.equals(decoName) ||
PyKnownDecoratorUtil.isPropertyDecorator(deco, TypeEvalContext.codeInsightFallback(getProject())) ||
qname.matches(decoratorName, PyNames.GETTER)) {
if (knownDecorator.isProperty()) {
getter = new Maybe<>(method);
break;
}
}
final QualifiedName qname = deco.getQualifiedName();
if (qname != null) {
if (qname.matches(decoratorName, PyNames.GETTER)) {
getter = new Maybe<>(method);
}
else if (qname.matches(decoratorName, PyNames.SETTER)) {
@@ -546,12 +546,14 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
*/
@Override
public @Nullable Modifier getModifier() {
final String deconame = getClassOrStaticMethodDecorator();
if (PyNames.CLASSMETHOD.equals(deconame)) {
return CLASSMETHOD;
}
else if (PyNames.STATICMETHOD.equals(deconame)) {
return STATICMETHOD;
final PyKnownDecorator decorator = getClassOrStaticMethodDecorator();
if (decorator != null) {
if (decorator.isClassMethod()) {
return CLASSMETHOD;
}
else if (decorator.isStaticMethod()) {
return STATICMETHOD;
}
}
final String funcName = getName();
@@ -574,12 +576,6 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
if (PyNames.CLASS_GETITEM.equals(funcName) && level.isAtLeast(LanguageLevel.PYTHON37)) {
return CLASSMETHOD;
}
final TypeEvalContext context = TypeEvalContext.codeInsightFallback(getProject());
for (PyKnownDecoratorUtil.KnownDecorator knownDecorator : PyKnownDecoratorUtil.getKnownDecorators(this, context)) {
if (knownDecorator == PyKnownDecoratorUtil.KnownDecorator.ABC_ABSTRACTCLASSMETHOD) return CLASSMETHOD;
if (knownDecorator == PyKnownDecoratorUtil.KnownDecorator.ABC_ABSTRACTSTATICMETHOD) return STATICMETHOD;
}
}
final PyFunctionStub stub = getStub();
@@ -709,21 +705,17 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
*
* @return name of the built-in decorator, or null (even if there are non-built-in decorators).
*/
private @Nullable String getClassOrStaticMethodDecorator() {
private @Nullable PyKnownDecorator getClassOrStaticMethodDecorator() {
PyDecoratorList decolist = getDecoratorList();
if (decolist != null) {
PyDecorator[] decos = decolist.getDecorators();
if (decos.length > 0) {
for (int i = decos.length - 1; i >= 0; i -= 1) {
PyDecorator deco = decos[i];
String deconame = deco.getName();
if (PyNames.CLASSMETHOD.equals(deconame) || PyNames.STATICMETHOD.equals(deconame)) {
return deconame;
}
for (PyKnownDecoratorProvider provider : PyKnownDecoratorProvider.EP_NAME.getIterable()) {
String name = provider.toKnownDecorator(deconame);
if (name != null) {
return name;
List<PyKnownDecorator> knownDecorators = PyKnownDecoratorUtil.asKnownDecorators(deco, TypeEvalContext.codeInsightFallback(getProject()));
for (PyKnownDecorator decorator : knownDecorators) {
if (decorator.isClassMethod() || decorator.isStaticMethod()) {
return decorator;
}
}
}
@@ -5,9 +5,9 @@ import com.intellij.psi.stubs.StubOutputStream
import com.intellij.psi.util.QualifiedName
import com.jetbrains.python.PyNames
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
import com.jetbrains.python.psi.PyKnownDecorator
import com.jetbrains.python.psi.PyCallExpression
import com.jetbrains.python.psi.PyClass
import com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator
import com.jetbrains.python.psi.PyReferenceExpression
import com.jetbrains.python.psi.PyTargetExpression
import com.jetbrains.python.psi.impl.PyPsiUtils
@@ -29,8 +29,8 @@ class PyEnumAttributeStubType : CustomTargetExpressionStubType<PyEnumAttributeSt
val argument = callExpr.arguments.singleOrNull() ?: return null
val isMember = when (calleeFqn) {
KnownDecorator.ENUM_MEMBER.qualifiedName -> true
KnownDecorator.ENUM_NONMEMBER.qualifiedName -> false
PyKnownDecorator.ENUM_MEMBER.qualifiedName -> true
PyKnownDecorator.ENUM_NONMEMBER.qualifiedName -> false
else -> return null
}
return PyEnumAttributeStubImpl(PyLiteralKind.fromExpression(argument), isMember)
@@ -2,6 +2,7 @@ package com.jetbrains.python.psi.stubs
import com.intellij.psi.stubs.StubInputStream
import com.intellij.psi.stubs.StubOutputStream
import com.jetbrains.python.psi.PyKnownDecorator
import com.jetbrains.python.psi.PyDecorator
import com.jetbrains.python.psi.PyKnownDecoratorUtil
import com.jetbrains.python.psi.PyReferenceExpression
@@ -33,7 +34,7 @@ class PyFunctoolsWrapsDecoratorStub(val wrapped: String) : PyCustomDecoratorStub
companion object {
fun create(psi: PyDecorator): PyFunctoolsWrapsDecoratorStub? {
val qName = psi.qualifiedName ?: return null
if (!PyKnownDecoratorUtil.asKnownDecorators(qName).contains(PyKnownDecoratorUtil.KnownDecorator.FUNCTOOLS_WRAPS)) return null
if (!PyKnownDecoratorUtil.asKnownDecorators(qName).contains(PyKnownDecorator.FUNCTOOLS_WRAPS)) return null
val wrappedExpr = psi.argumentList?.getValueExpressionForParam(PyKnownDecoratorUtil.FunctoolsWrapsParameters.WRAPPED) as? PyReferenceExpression
val wrappedExprQName = wrappedExpr?.asQualifiedName() ?: return null
return PyFunctoolsWrapsDecoratorStub(wrappedExprQName.toString())
@@ -112,7 +112,7 @@ public final class PyiUtil {
}
public static boolean isOverload(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
final PyKnownDecoratorUtil.KnownDecorator overload = PyKnownDecoratorUtil.KnownDecorator.TYPING_OVERLOAD;
final PyKnownDecorator overload = PyKnownDecorator.TYPING_OVERLOAD;
return element instanceof PyFunction &&
PyKnownDecoratorUtil.getKnownDecorators((PyFunction)element, context).contains(overload);