Skip to content

Commit

Permalink
Added support for @no_type_check decorator. This addresses #1448. (#6…
Browse files Browse the repository at this point in the history
…987)
  • Loading branch information
erictraut authored Jan 15, 2024
1 parent 56fe9e1 commit 80b5961
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 11 deletions.
2 changes: 2 additions & 0 deletions packages/pyright-internal/src/analyzer/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export function getFunctionInfoFromDecorators(
flags |= FunctionTypeFlags.Overridden;
} else if (decoratorType.details.builtInName === 'type_check_only') {
flags |= FunctionTypeFlags.TypeCheckOnly;
} else if (decoratorType.details.builtInName === 'no_type_check') {
flags |= FunctionTypeFlags.NoTypeCheck;
} else if (decoratorType.details.builtInName === 'overload') {
flags |= FunctionTypeFlags.Overloaded;
}
Expand Down
39 changes: 28 additions & 11 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3163,17 +3163,30 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
return undefined;
}

// Should we suppress this diagnostic because it's within an unannotated function?
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
if (!fileInfo.diagnosticRuleSet.analyzeUnannotatedFunctions) {
const containingFunction = ParseTreeUtils.getEnclosingFunction(node);
const containingFunction = ParseTreeUtils.getEnclosingFunction(node);

// Is the target node within the body of the function? If so, suppress the diagnostic.
if (
containingFunction &&
ParseTreeUtils.isUnannotatedFunction(containingFunction) &&
ParseTreeUtils.isNodeContainedWithin(node, containingFunction.suite)
) {
if (containingFunction) {
// Should we suppress this diagnostic because it's within an unannotated function?
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
if (!fileInfo.diagnosticRuleSet.analyzeUnannotatedFunctions) {
// Is the target node within the body of the function? If so, suppress the diagnostic.
if (
ParseTreeUtils.isUnannotatedFunction(containingFunction) &&
ParseTreeUtils.isNodeContainedWithin(node, containingFunction.suite)
) {
return undefined;
}
}

// Should we suppress this diagnostic because it's within a no_type_check function?
const containingClassNode = ParseTreeUtils.getEnclosingClass(containingFunction, /* stopAtFunction */ true);
const functionInfo = getFunctionInfoFromDecorators(
evaluatorInterface,
containingFunction,
!!containingClassNode
);

if ((functionInfo.flags & FunctionTypeFlags.NoTypeCheck) !== 0) {
return undefined;
}
}
Expand Down Expand Up @@ -17630,7 +17643,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}

if (paramTypeNode) {
annotatedType = getTypeOfParameterAnnotation(paramTypeNode, param.category);
if ((functionInfo.flags & FunctionTypeFlags.NoTypeCheck) !== 0) {
annotatedType = UnknownType.create();
} else {
annotatedType = getTypeOfParameterAnnotation(paramTypeNode, param.category);
}

if (isVariadicTypeVar(annotatedType) && !annotatedType.isVariadicUnpacked) {
addError(
Expand Down
3 changes: 3 additions & 0 deletions packages/pyright-internal/src/analyzer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,9 @@ export const enum FunctionTypeFlags {

// Decorated with @override as defined in PEP 698.
Overridden = 1 << 18,

// Decorated with @no_type_check.
NoTypeCheck = 1 << 19,
}

interface FunctionDetails {
Expand Down
28 changes: 28 additions & 0 deletions packages/pyright-internal/src/tests/samples/noTypeCheck1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This sample tests the handling of the @no_type_check decorator.


from typing import no_type_check


@no_type_check
class A:
# This should generate an error because no_type_check has
# no effect when applied to a class.
x: int = ""


@no_type_check
def func1(a: int, b: int(), *args, c: int = 3) -> dummy:
x: int = ""


reveal_type(
func1,
expected_text="(a: Unknown, b: Unknown, *args: Unknown, c: Unknown = 3) -> Unknown",
)


# This should generate an error.
func1()

func1("", "", c="")
5 changes: 5 additions & 0 deletions packages/pyright-internal/src/tests/typeEvaluator5.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,8 @@ test('TypeCheckOnly1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeCheckOnly1.py']);
TestUtils.validateResults(analysisResults, 4);
});

test('NoTypeCheck1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['noTypeCheck1.py']);
TestUtils.validateResults(analysisResults, 2);
});

0 comments on commit 80b5961

Please sign in to comment.