mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Added check for except*
to enforce that exception class does not derive from BaseExceptionGroup
.
This commit is contained in:
parent
2ade792ba8
commit
16ea2a80c5
@ -1145,7 +1145,7 @@ export class Checker extends ParseTreeWalker {
|
||||
|
||||
const exceptionType = this._evaluator.getType(node.typeExpression);
|
||||
if (exceptionType) {
|
||||
this._validateExceptionType(exceptionType, node.typeExpression);
|
||||
this._validateExceptionType(exceptionType, node.typeExpression, node.isExceptGroup);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2943,7 +2943,9 @@ export class Checker extends ParseTreeWalker {
|
||||
exceptionType: Type,
|
||||
diag: DiagnosticAddendum,
|
||||
baseExceptionType: Type | undefined,
|
||||
allowTuple: boolean
|
||||
baseExceptionGroupType: Type | undefined,
|
||||
allowTuple: boolean,
|
||||
isExceptGroup: boolean
|
||||
) {
|
||||
const derivesFromBaseException = (classType: ClassType) => {
|
||||
if (!baseExceptionType || !isInstantiableClass(baseExceptionType)) {
|
||||
@ -2953,6 +2955,14 @@ export class Checker extends ParseTreeWalker {
|
||||
return derivesFromClassRecursive(classType, baseExceptionType, /* ignoreUnknown */ false);
|
||||
};
|
||||
|
||||
const derivesFromBaseExceptionGroup = (classType: ClassType) => {
|
||||
if (!baseExceptionGroupType || !isInstantiableClass(baseExceptionGroupType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return derivesFromClassRecursive(classType, baseExceptionGroupType, /* ignoreUnknown */ false);
|
||||
};
|
||||
|
||||
doForEachSubtype(exceptionType, (exceptionSubtype) => {
|
||||
if (isAnyOrUnknown(exceptionSubtype)) {
|
||||
return;
|
||||
@ -2967,6 +2977,10 @@ export class Checker extends ParseTreeWalker {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (isExceptGroup && derivesFromBaseExceptionGroup(exceptionSubtype)) {
|
||||
diag.addMessage(LocMessage.exceptionGroupTypeIncorrect());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2976,7 +2990,9 @@ export class Checker extends ParseTreeWalker {
|
||||
typeArg.type,
|
||||
diag,
|
||||
baseExceptionType,
|
||||
/* allowTuple */ false
|
||||
baseExceptionGroupType,
|
||||
/* allowTuple */ false,
|
||||
isExceptGroup
|
||||
);
|
||||
});
|
||||
return;
|
||||
@ -2991,11 +3007,19 @@ export class Checker extends ParseTreeWalker {
|
||||
});
|
||||
}
|
||||
|
||||
private _validateExceptionType(exceptionType: Type, errorNode: ExpressionNode): void {
|
||||
private _validateExceptionType(exceptionType: Type, errorNode: ExpressionNode, isExceptGroup: boolean): void {
|
||||
const baseExceptionType = this._evaluator.getBuiltInType(errorNode, 'BaseException');
|
||||
const baseExceptionGroupType = this._evaluator.getBuiltInType(errorNode, 'BaseExceptionGroup');
|
||||
const diagAddendum = new DiagnosticAddendum();
|
||||
|
||||
this._validateExceptionTypeRecursive(exceptionType, diagAddendum, baseExceptionType, /* allowTuple */ true);
|
||||
this._validateExceptionTypeRecursive(
|
||||
exceptionType,
|
||||
diagAddendum,
|
||||
baseExceptionType,
|
||||
baseExceptionGroupType,
|
||||
/* allowTuple */ true,
|
||||
isExceptGroup
|
||||
);
|
||||
|
||||
if (!diagAddendum.isEmpty()) {
|
||||
this._evaluator.addDiagnostic(
|
||||
|
@ -442,6 +442,7 @@ export namespace Localizer {
|
||||
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.enumMemberSet'));
|
||||
export const enumMemberTypeAnnotation = () => getRawString('Diagnostic.enumMemberTypeAnnotation');
|
||||
export const exceptionGroupIncompatible = () => getRawString('Diagnostic.exceptionGroupIncompatible');
|
||||
export const exceptionGroupTypeIncorrect = () => getRawString('Diagnostic.exceptionGroupTypeIncorrect');
|
||||
export const exceptionTypeIncorrect = () =>
|
||||
new ParameterizedString<{ type: string }>(getRawString('Diagnostic.exceptionTypeIncorrect'));
|
||||
export const exceptionTypeNotClass = () =>
|
||||
|
@ -145,6 +145,7 @@
|
||||
"enumMemberSet": "Enum member \"{name}\" cannot be assigned",
|
||||
"enumMemberTypeAnnotation": "Type annotations are not allowed for enum members",
|
||||
"exceptionGroupIncompatible": "Exception group syntax (\"except*\") requires Python 3.11 or newer",
|
||||
"exceptionGroupTypeIncorrect": "Exception type in except* cannot derive from BaseGroupException",
|
||||
"exceptionTypeIncorrect": "\"{type}\" does not derive from BaseException",
|
||||
"exceptionTypeNotClass": "\"{type}\" is not a valid exception class",
|
||||
"exceptionTypeNotInstantiable": "Constructor for exception type \"{type}\" requires one or more arguments",
|
||||
|
@ -0,0 +1,31 @@
|
||||
# This sample tests the syntax handling for Python 3.11 exception groups
|
||||
# as described in PEP 654.
|
||||
|
||||
|
||||
def func1():
|
||||
|
||||
try:
|
||||
pass
|
||||
|
||||
# This should generate an error if using Python 3.10 or earlier.
|
||||
except* ValueError as e:
|
||||
reveal_type(e, expected_text="BaseExceptionGroup[ValueError]")
|
||||
pass
|
||||
|
||||
# This should generate an error if using Python 3.10 or earlier.
|
||||
except*:
|
||||
pass
|
||||
|
||||
|
||||
def func2():
|
||||
try:
|
||||
pass
|
||||
# This should generate an error because ExceptionGroup derives
|
||||
# from BaseExceptionGroup.
|
||||
except* ExceptionGroup as e:
|
||||
pass
|
||||
|
||||
# This should generate an error because ExceptionGroup derives
|
||||
# from BaseExceptionGroup.
|
||||
except* (ValueError, ExceptionGroup) as e:
|
||||
pass
|
@ -901,18 +901,6 @@ test('TryExcept6', () => {
|
||||
TestUtils.validateResults(analysisResults, 1);
|
||||
});
|
||||
|
||||
test('TryExcept7', () => {
|
||||
const configOptions = new ConfigOptions(Uri.empty());
|
||||
|
||||
configOptions.defaultPythonVersion = pythonVersion3_10;
|
||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['tryExcept7.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults1, 3);
|
||||
|
||||
configOptions.defaultPythonVersion = pythonVersion3_11;
|
||||
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['tryExcept7.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults2, 0);
|
||||
});
|
||||
|
||||
test('TryExcept8', () => {
|
||||
const configOptions = new ConfigOptions(Uri.empty());
|
||||
|
||||
@ -936,6 +924,18 @@ test('TryExcept11', () => {
|
||||
TestUtils.validateResults(analysisResults, 0);
|
||||
});
|
||||
|
||||
test('exceptionGroup1', () => {
|
||||
const configOptions = new ConfigOptions(Uri.empty());
|
||||
|
||||
configOptions.defaultPythonVersion = pythonVersion3_10;
|
||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['exceptionGroup1.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults1, 9);
|
||||
|
||||
configOptions.defaultPythonVersion = pythonVersion3_11;
|
||||
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['exceptionGroup1.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults2, 2);
|
||||
});
|
||||
|
||||
test('Del1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['del1.py']);
|
||||
TestUtils.validateResults(analysisResults, 6);
|
||||
|
Loading…
Reference in New Issue
Block a user