mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Added support for @deprecation messages for decorators that wrap a function in a callable object. The deprecated message is now propagated through the ParamSpec and the __call__
method. This addresses #7732.
This commit is contained in:
parent
0127fd67ad
commit
c7168d7068
@ -4187,6 +4187,11 @@ export class Checker extends ParseTreeWalker {
|
||||
errorMessage = LocMessage.deprecatedConstructor().format({
|
||||
name: type.details.name,
|
||||
});
|
||||
} else if (isClassInstance(type) && overload.details.name === '__call__') {
|
||||
deprecatedMessage = overload.details.deprecatedMessage;
|
||||
errorMessage = LocMessage.deprecatedFunction().format({
|
||||
name: node.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -4202,13 +4207,20 @@ export class Checker extends ParseTreeWalker {
|
||||
) {
|
||||
deprecatedMessage = subtype.details.deprecatedMessage;
|
||||
errorMessage = LocMessage.deprecatedClass().format({ name: subtype.details.name });
|
||||
} else {
|
||||
// See if this is part of a call to a constructor.
|
||||
getDeprecatedMessageForOverloadedCall(this._evaluator, subtype);
|
||||
return;
|
||||
}
|
||||
} else if (isFunction(subtype)) {
|
||||
|
||||
getDeprecatedMessageForOverloadedCall(this._evaluator, subtype);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFunction(subtype)) {
|
||||
if (subtype.details.deprecatedMessage !== undefined) {
|
||||
if (!subtype.details.name || node.value === subtype.details.name) {
|
||||
if (
|
||||
!subtype.details.name ||
|
||||
subtype.details.name === '__call__' ||
|
||||
node.value === subtype.details.name
|
||||
) {
|
||||
deprecatedMessage = subtype.details.deprecatedMessage;
|
||||
errorMessage = getDeprecatedMessageForFunction(subtype);
|
||||
}
|
||||
|
@ -3420,6 +3420,8 @@ export function convertTypeToParamSpecValue(type: Type): FunctionType {
|
||||
type.details.docString
|
||||
);
|
||||
|
||||
newFunction.details.deprecatedMessage = type.details.deprecatedMessage;
|
||||
|
||||
type.details.parameters.forEach((param, index) => {
|
||||
FunctionType.addParameter(newFunction, {
|
||||
category: param.category,
|
||||
@ -3482,6 +3484,7 @@ export function convertParamSpecValueToType(paramSpecValue: FunctionType, omitPa
|
||||
functionType.details.paramSpec = paramSpecValue.details.paramSpec;
|
||||
}
|
||||
functionType.details.docString = paramSpecValue.details.docString;
|
||||
functionType.details.deprecatedMessage = paramSpecValue.details.deprecatedMessage;
|
||||
|
||||
return functionType;
|
||||
}
|
||||
|
@ -1934,10 +1934,15 @@ export namespace FunctionType {
|
||||
});
|
||||
|
||||
newFunction.details.paramSpec = paramSpecValue.details.paramSpec;
|
||||
|
||||
if (!newFunction.details.docString) {
|
||||
newFunction.details.docString = paramSpecValue.details.docString;
|
||||
}
|
||||
|
||||
if (!newFunction.details.deprecatedMessage) {
|
||||
newFunction.details.deprecatedMessage = paramSpecValue.details.deprecatedMessage;
|
||||
}
|
||||
|
||||
FunctionType.addHigherOrderTypeVarScopeIds(newFunction, paramSpecValue.details.typeVarScopeId);
|
||||
|
||||
return newFunction;
|
||||
|
@ -585,3 +585,14 @@ test('Deprecated5', () => {
|
||||
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['deprecated5.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults2, 2);
|
||||
});
|
||||
|
||||
test('Deprecated6', () => {
|
||||
const configOptions = new ConfigOptions(Uri.empty());
|
||||
|
||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated6.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 3);
|
||||
|
||||
configOptions.diagnosticRuleSet.reportDeprecated = 'error';
|
||||
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['deprecated6.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults2, 3);
|
||||
});
|
||||
|
50
packages/pyright-internal/src/tests/samples/deprecated6.py
Normal file
50
packages/pyright-internal/src/tests/samples/deprecated6.py
Normal file
@ -0,0 +1,50 @@
|
||||
# This sample tests the case where a __call__ is marked deprecated.
|
||||
|
||||
from typing import Callable, Generic, ParamSpec, TypeVar
|
||||
from typing_extensions import deprecated # pyright: ignore[reportMissingModuleSource]
|
||||
|
||||
|
||||
class A:
|
||||
@deprecated("Use ClassB instead")
|
||||
def __call__(self) -> None: ...
|
||||
|
||||
|
||||
a = A()
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
a()
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
class B(Generic[P, R]):
|
||||
def __init__(self, cb: Callable[P, R]) -> None:
|
||||
self.cb = cb
|
||||
|
||||
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
||||
return self.cb(*args, **kwargs)
|
||||
|
||||
|
||||
@B
|
||||
@deprecated("Don't use this.")
|
||||
def func1(x: int) -> None:
|
||||
pass
|
||||
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
func1(3)
|
||||
|
||||
|
||||
def deco1(cb: Callable[P, R]) -> B[P, R]:
|
||||
return B(cb)
|
||||
|
||||
|
||||
@deco1
|
||||
@deprecated("Don't use this.")
|
||||
def func2(x: int) -> None:
|
||||
pass
|
||||
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
func2(3)
|
Loading…
Reference in New Issue
Block a user