mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Added support for @deprecated
in an __init__
method when constructing a class. This addresses https://github.com/microsoft/pyright/issues/4456.
This commit is contained in:
parent
575adcb29a
commit
7ad4a8ead5
@ -3656,6 +3656,40 @@ export class Checker extends ParseTreeWalker {
|
||||
let errorMessage: string | undefined;
|
||||
let deprecatedMessage: string | undefined;
|
||||
|
||||
function getDeprecatedMessageForOverloadedCall(evaluator: TypeEvaluator, type: Type) {
|
||||
// Determine if the node is part of a call expression. If so,
|
||||
// we can determine which overload(s) were used to satisfy
|
||||
// the call expression and determine whether any of them
|
||||
// are deprecated.
|
||||
const callNode = ParseTreeUtils.getCallForName(node);
|
||||
|
||||
if (callNode) {
|
||||
const callTypeResult = evaluator.getTypeResult(callNode);
|
||||
|
||||
if (
|
||||
callTypeResult &&
|
||||
callTypeResult.overloadsUsedForCall &&
|
||||
callTypeResult.overloadsUsedForCall.length > 0
|
||||
) {
|
||||
callTypeResult.overloadsUsedForCall.forEach((overload) => {
|
||||
if (overload.details.deprecatedMessage !== undefined) {
|
||||
if (node.value === overload.details.name) {
|
||||
deprecatedMessage = overload.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedFunction().format({
|
||||
name: overload.details.name,
|
||||
});
|
||||
} else if (isInstantiableClass(type) && overload.details.name === '__init__') {
|
||||
deprecatedMessage = overload.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedConstructor().format({
|
||||
name: type.details.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doForEachSubtype(type, (subtype) => {
|
||||
if (isClass(subtype)) {
|
||||
if (
|
||||
@ -3664,39 +3698,24 @@ export class Checker extends ParseTreeWalker {
|
||||
node.value === subtype.details.name
|
||||
) {
|
||||
deprecatedMessage = subtype.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedClass();
|
||||
errorMessage = Localizer.Diagnostic.deprecatedClass().format({ name: subtype.details.name });
|
||||
} else {
|
||||
// See if this is part of a call to a constructor.
|
||||
getDeprecatedMessageForOverloadedCall(this._evaluator, subtype);
|
||||
}
|
||||
} else if (isFunction(subtype)) {
|
||||
if (subtype.details.deprecatedMessage !== undefined && node.value === subtype.details.name) {
|
||||
deprecatedMessage = subtype.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedFunction();
|
||||
errorMessage = Localizer.Diagnostic.deprecatedFunction().format({
|
||||
name: subtype.details.name || '<anonymous>',
|
||||
});
|
||||
}
|
||||
} else if (isOverloadedFunction(subtype)) {
|
||||
// Determine if the node is part of a call expression. If so,
|
||||
// we can determine which overload(s) were used to satisfy
|
||||
// the call expression and determine whether any of them
|
||||
// are deprecated.
|
||||
const callNode = ParseTreeUtils.getCallForName(node);
|
||||
|
||||
if (callNode) {
|
||||
const callTypeResult = this._evaluator.getTypeResult(callNode);
|
||||
|
||||
if (
|
||||
callTypeResult &&
|
||||
callTypeResult.overloadsUsedForCall &&
|
||||
callTypeResult.overloadsUsedForCall.length > 0
|
||||
) {
|
||||
callTypeResult.overloadsUsedForCall.forEach((overload) => {
|
||||
if (
|
||||
overload.details.deprecatedMessage !== undefined &&
|
||||
node.value === overload.details.name
|
||||
) {
|
||||
deprecatedMessage = overload.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedFunction();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
getDeprecatedMessageForOverloadedCall(this._evaluator, subtype);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -328,8 +328,12 @@ export namespace Localizer {
|
||||
export const declaredReturnTypeUnknown = () => getRawString('Diagnostic.declaredReturnTypeUnknown');
|
||||
export const defaultValueContainsCall = () => getRawString('Diagnostic.defaultValueContainsCall');
|
||||
export const defaultValueNotAllowed = () => getRawString('Diagnostic.defaultValueNotAllowed');
|
||||
export const deprecatedClass = () => getRawString('Diagnostic.deprecatedClass');
|
||||
export const deprecatedFunction = () => getRawString('Diagnostic.deprecatedFunction');
|
||||
export const deprecatedClass = () =>
|
||||
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.deprecatedClass'));
|
||||
export const deprecatedConstructor = () =>
|
||||
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.deprecatedConstructor'));
|
||||
export const deprecatedFunction = () =>
|
||||
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.deprecatedFunction'));
|
||||
export const deprecatedType = () =>
|
||||
new ParameterizedString<{ version: string; replacement: string }>(
|
||||
getRawString('Diagnostic.deprecatedType')
|
||||
|
@ -91,8 +91,9 @@
|
||||
"declaredReturnTypeUnknown": "Declared return type is unknown",
|
||||
"defaultValueContainsCall": "Function calls and mutable objects not allowed within parameter default value expression",
|
||||
"defaultValueNotAllowed": "Parameter with \"*\" or \"**\" cannot have default value",
|
||||
"deprecatedClass": "This class is deprecated",
|
||||
"deprecatedFunction": "This function is deprecated",
|
||||
"deprecatedClass": "The class \"{name}\" is deprecated",
|
||||
"deprecatedConstructor": "The constructor for class \"{name}\" is deprecated",
|
||||
"deprecatedFunction": "This function \"{name}\" is deprecated",
|
||||
"deprecatedType": "This type is deprecated as of Python {version}; use \"{replacement}\" instead",
|
||||
"delTargetExpr": "Expression cannot be deleted",
|
||||
"dictExpandIllegalInComprehension": "Dictionary expansion not allowed in comprehension",
|
||||
|
@ -504,11 +504,11 @@ test('Deprecated2', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated2.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 5);
|
||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 6);
|
||||
|
||||
configOptions.diagnosticRuleSet.reportDeprecated = 'error';
|
||||
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['deprecated2.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults2, 5);
|
||||
TestUtils.validateResults(analysisResults2, 6);
|
||||
});
|
||||
|
||||
test('Deprecated3', () => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
# This sample tests the @typing.deprecated decorator introduced in PEP 702.
|
||||
|
||||
from typing import Self
|
||||
from typing_extensions import deprecated, overload
|
||||
|
||||
|
||||
@ -69,3 +70,23 @@ func2("hi")
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
func2(3)
|
||||
|
||||
|
||||
class ClassD:
|
||||
@overload
|
||||
def __init__(self, x: int) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
@deprecated("str no longer supported")
|
||||
def __init__(self, x: str) -> None:
|
||||
...
|
||||
|
||||
def __init__(self, x: int | str) -> None:
|
||||
...
|
||||
|
||||
|
||||
ClassD(3)
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
ClassD("")
|
||||
|
Loading…
Reference in New Issue
Block a user