| [case testAnnotateNonNativeAttribute] | |
| from typing import Any | |
| def f1(x): | |
| return x.foo # A: Get non-native attribute "foo". | |
| def f2(x: Any) -> object: | |
| return x.foo # A: Get non-native attribute "foo". | |
| def f3(x): | |
| x.bar = 1 # A: Set non-native attribute "bar". | |
| class C: | |
| foo: int | |
| def method(self) -> int: | |
| return self.foo | |
| def good1(x: C) -> int: | |
| return x.foo | |
| [case testAnnotateMethod] | |
| class C: | |
| def method(self, x): | |
| return x + "y" # A: Generic "+" operation. | |
| [case testAnnotateGenericBinaryOperations] | |
| def generic_add(x): | |
| return x + 1 # A: Generic "+" operation. | |
| def generic_sub(x): | |
| return x - 1 # A: Generic "-" operation. | |
| def generic_mul(x): | |
| return x * 1 # A: Generic "*" operation. | |
| def generic_div(x): | |
| return x / 1 # A: Generic "/" operation. | |
| def generic_floor_div(x): | |
| return x // 1 # A: Generic "//" operation. | |
| def generic_unary_plus(x): | |
| return +x # A: Generic unary "+" operation. | |
| def generic_unary_minus(x): | |
| return -x # A: Generic unary "-" operation. | |
| def native_int_ops(x: int, y: int) -> int: | |
| a = x + 1 - y | |
| return x * a // y | |
| [case testAnnotateGenericBitwiseOperations] | |
| def generic_and(x): | |
| return x & 1 # A: Generic "&" operation. | |
| def generic_or(x): | |
| return x | 1 # A: Generic "|" operation. | |
| def generic_xor(x): | |
| return x ^ 1 # A: Generic "^" operation. | |
| def generic_left_shift(x): | |
| return x << 1 # A: Generic "<<" operation. | |
| def generic_right_shift(x): | |
| return x >> 1 # A: Generic ">>" operation. | |
| def generic_invert(x): | |
| return ~x # A: Generic "~" operation. | |
| def native_int_ops(x: int, y: int) -> int: | |
| a = (x & 1) << y | |
| return (x | a) >> (y ^ 1) | |
| [case testAnnotateGenericComparisonOperations] | |
| def generic_eq(x, y): | |
| return x == y # A: Generic comparison operation. | |
| def generic_ne(x, y): | |
| return x != y # A: Generic comparison operation. | |
| def generic_lt(x, y): | |
| return x < y # A: Generic comparison operation. | |
| def generic_le(x, y): | |
| return x <= y # A: Generic comparison operation. | |
| def generic_gt(x, y): | |
| return x > y # A: Generic comparison operation. | |
| def generic_ge(x, y): | |
| return x >= y # A: Generic comparison operation. | |
| def int_comparisons(x: int, y: int) -> int: | |
| if x == y: | |
| return 0 | |
| if x < y: | |
| return 1 | |
| if x > y: | |
| return 2 | |
| return 3 | |
| [case testAnnotateTwoOperationsOnLine] | |
| def f(x): | |
| return x.foo + 1 # A: Get non-native attribute "foo". Generic "+" operation. | |
| [case testAnnotateNonNativeMethod] | |
| from typing import Any | |
| def f1(x): | |
| return x.foo() # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). | |
| def f2(x: Any) -> None: | |
| x.foo(1) # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). | |
| x.foo(a=1) # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). | |
| t = (1, 'x') | |
| x.foo(*t) # A: Get non-native attribute "foo". Generic call operation. | |
| d = {"a": 1} | |
| x.foo(*d) # A: Get non-native attribute "foo". Generic call operation. | |
| class C: | |
| def foo(self) -> int: | |
| return 0 | |
| def g(c: C) -> int: | |
| return c.foo() | |
| [case testAnnotateGlobalVariableAccess] | |
| from typing import Final | |
| import nonnative | |
| x = 0 | |
| y: Final = 0 | |
| def read() -> int: | |
| return x # A: Access global "x" through namespace dictionary (hint: access is faster if you can make it Final). | |
| def assign(a: int) -> None: | |
| global x | |
| x = a # A: Access global "x" through namespace dictionary (hint: access is faster if you can make it Final). | |
| def read_final() -> int: | |
| return y | |
| def read_nonnative() -> int: | |
| return nonnative.z # A: Get non-native attribute "z". | |
| [file nonnative.py] | |
| z = 2 | |
| [case testAnnotateNestedFunction] | |
| def f1() -> None: | |
| def g() -> None: # A: A nested function object is allocated each time statement is executed. A module-level function would be faster. | |
| pass | |
| g() | |
| def f2() -> int: | |
| l = lambda: 1 # A: A new object is allocated for lambda each time it is evaluated. A module-level function would be faster. | |
| return l() | |
| [case testAnnotateGetSetItem] | |
| from typing import List, Dict | |
| def f1(x, y): | |
| return x[y] # A: Generic indexing operation. | |
| def f2(x, y, z): | |
| x[y] = z # A: Generic indexed assignment. | |
| def list_get_item(x: List[int], y: int) -> int: | |
| return x[y] | |
| def list_set_item(x: List[int], y: int) -> None: | |
| x[y] = 5 | |
| def dict_get_item(d: Dict[str, str]) -> str: | |
| return d['x'] | |
| def dict_set_item(d: Dict[str, str]) -> None: | |
| d['x'] = 'y' | |
| [case testAnnotateStrMethods] | |
| def startswith(x: str) -> bool: | |
| return x.startswith('foo') | |
| def islower(x: str) -> bool: | |
| return x.islower() # A: Call non-native method "islower" (it may be defined in a non-native class, or decorated). | |
| [case testAnnotateSpecificStdlibFeatures] | |
| import functools | |
| import itertools | |
| from functools import partial | |
| from itertools import chain, groupby, islice | |
| def f(x: int, y: int) -> None: pass | |
| def use_partial1() -> None: | |
| p = partial(f, 1) # A: "functools.partial" is inefficient in compiled code. | |
| p(2) | |
| def use_partial2() -> None: | |
| p = functools.partial(f, 1) # A: "functools.partial" is inefficient in compiled code. | |
| p(2) | |
| def use_chain1() -> None: | |
| for x in chain([1, 3], [4, 5]): # A: "itertools.chain" is inefficient in compiled code (hint: replace with for loops). | |
| pass | |
| def use_chain2() -> None: | |
| for x in itertools.chain([1, 3], [4, 5]): # A: "itertools.chain" is inefficient in compiled code (hint: replace with for loops). | |
| pass | |
| def use_groupby1() -> None: | |
| for a, b in groupby([('A', 'B')]): # A: "itertools.groupby" is inefficient in compiled code. | |
| pass | |
| def use_groupby2() -> None: | |
| for a, b in itertools.groupby([('A', 'B')]): # A: "itertools.groupby" is inefficient in compiled code. | |
| pass | |
| def use_islice() -> None: | |
| for x in islice([1, 2, 3], 1, 2): # A: "itertools.islice" is inefficient in compiled code (hint: replace with for loop over index range). | |
| pass | |
| [case testAnnotateGenericForLoop] | |
| from typing import Iterable, Sequence, Iterator, List | |
| def f1(a): | |
| for x in a: # A: For loop uses generic operations (iterable has type "Any"). | |
| pass | |
| def f2(a: Iterable[str]) -> None: | |
| for x in a: # A: For loop uses generic operations (iterable has the abstract type "typing.Iterable"). | |
| pass | |
| def f3(a: Sequence[str]) -> None: | |
| for x in a: # A: For loop uses generic operations (iterable has the abstract type "typing.Sequence"). | |
| pass | |
| def f4(a: Iterator[str]) -> None: | |
| for x in a: # A: For loop uses generic operations (iterable has the abstract type "typing.Iterator"). | |
| pass | |
| def good1(a: List[str]) -> None: | |
| for x in a: | |
| pass | |
| class C: | |
| def __iter__(self) -> Iterator[str]: | |
| assert False | |
| def good2(a: List[str]) -> None: | |
| for x in a: | |
| pass | |
| [case testAnnotateGenericComprehensionOrGenerator] | |
| from typing import List, Iterable | |
| def f1(a): | |
| return [x for x in a] # A: Comprehension or generator uses generic operations (iterable has type "Any"). | |
| def f2(a: Iterable[int]): | |
| return {x for x in a} # A: Comprehension or generator uses generic operations (iterable has the abstract type "typing.Iterable"). | |
| def f3(a): | |
| return {x: 1 for x in a} # A: Comprehension uses generic operations (iterable has type "Any"). | |
| def f4(a): | |
| return (x for x in a) # A: Comprehension or generator uses generic operations (iterable has type "Any"). | |
| def good1(a: List[int]) -> List[int]: | |
| return [x + 1 for x in a] | |
| [case testAnnotateIsinstance] | |
| from typing import Protocol, runtime_checkable, Union | |
| @runtime_checkable | |
| class P(Protocol): | |
| def foo(self) -> None: ... | |
| class C: pass | |
| class D(C): | |
| def bar(self) -> None: pass | |
| def bad1(x: object) -> bool: | |
| return isinstance(x, P) # A: Expensive isinstance() check against protocol "P". | |
| def bad2(x: object) -> bool: | |
| return isinstance(x, (str, P)) # A: Expensive isinstance() check against protocol "P". | |
| def good1(x: C) -> bool: | |
| if isinstance(x, D): | |
| x.bar() | |
| return isinstance(x, D) | |
| def good2(x: Union[int, str]) -> int: | |
| if isinstance(x, int): | |
| return x + 1 | |
| else: | |
| return int(x + "1") | |
| [typing fixtures/typing-full.pyi] | |
| [case testAnnotateDeepcopy] | |
| from typing import Any | |
| import copy | |
| def f(x: Any) -> Any: | |
| return copy.deepcopy(x) # A: "copy.deepcopy" tends to be slow. Make a shallow copy if possible. | |
| [case testAnnotateContextManager] | |
| from typing import Iterator | |
| from contextlib import contextmanager | |
| @contextmanager | |
| def slow_ctx_manager() -> Iterator[None]: | |
| yield | |
| class FastCtxManager: | |
| def __enter__(self) -> None: pass | |
| def __exit__(self, a, b, c) -> None: pass | |
| def f1(x) -> None: | |
| with slow_ctx_manager(): # A: "slow_ctx_manager" uses @contextmanager, which is slow in compiled code. Use a native class with "__enter__" and "__exit__" methods instead. | |
| x.foo # A: Get non-native attribute "foo". | |
| def f2(x) -> None: | |
| with FastCtxManager(): | |
| x.foo # A: Get non-native attribute "foo". | |
| [case testAnnotateAvoidNoiseAtTopLevel] | |
| from typing import Final | |
| class C(object): | |
| x = "s" | |
| y: Final = 1 | |
| x = "s" | |
| y: Final = 1 | |
| def f1() -> None: | |
| x = object # A: Get non-native attribute "object". | |
| [case testAnnotateCreateNonNativeInstance] | |
| from typing import NamedTuple | |
| from dataclasses import dataclass | |
| from nonnative import C | |
| def f1() -> None: | |
| c = C() # A: Creating an instance of non-native class "C" is slow. | |
| c.foo() # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). | |
| class NT(NamedTuple): | |
| x: int | |
| y: str | |
| def f2() -> int: | |
| o = NT(1, "x") # A: Creating an instance of non-native class "NT" is slow. | |
| return o.x | |
| def f3() -> int: | |
| o = NT(x=1, y="x") # A: Creating an instance of non-native class "NT" is slow. | |
| a, b = o | |
| return a | |
| @dataclass | |
| class D: | |
| x: int | |
| def f4() -> int: | |
| o = D(1) # A: Class "D" is only partially native, and constructing an instance is slow. | |
| return o.x | |
| class Nat: | |
| x: int | |
| class Deriv(Nat): | |
| def __init__(self, y: int) -> None: | |
| self.y = y | |
| def good1() -> int: | |
| n = Nat() | |
| d = Deriv(y=1) | |
| return n.x + d.x + d.y | |
| [file nonnative.py] | |
| class C: | |
| def foo(self) -> None: pass | |
| [case testAnnotateGetAttrAndSetAttrBuiltins] | |
| def f1(x, s: str): | |
| return getattr("x", s) # A: Dynamic attribute lookup. | |
| def f2(x, s: str): | |
| setattr(x, s, None) # A: Dynamic attribute set. | |
| [case testAnnotateSpecialAssignments] | |
| from typing import TypeVar, NamedTuple, List, TypedDict, NewType | |
| # Even though these are slow, we don't complain about them since there is generally | |
| # no better way (and at module top level these are very unlikely to be bottlenecks) | |
| A = List[int] | |
| T = TypeVar("T", bound=List[int]) | |
| NT = NamedTuple("NT", [("x", List[int])]) | |
| TD = TypedDict("TD", {"x": List[int]}) | |
| New = NewType("New", List[int]) | |
| [typing fixtures/typing-full.pyi] | |
| [case testAnnotateCallDecoratedNativeFunctionOrMethod] | |
| from typing import TypeVar, Callable, Any | |
| F = TypeVar("F", bound=Callable[..., Any]) | |
| def mydeco(f: F) -> F: | |
| return f | |
| @mydeco | |
| def d(x: int) -> int: | |
| return x | |
| def f1() -> int: | |
| return d(1) # A: Calling a decorated function ("d") is inefficient, even if it's native. | |
| class C: | |
| @mydeco | |
| def d(self) -> None: | |
| pass | |
| def f2() -> None: | |
| c = C() | |
| c.d() # A: Call non-native method "d" (it may be defined in a non-native class, or decorated). | |
| [case testAnnotateCallDifferentKindsOfMethods] | |
| from abc import ABC, abstractmethod | |
| class C: | |
| @staticmethod | |
| def s() -> None: ... | |
| @classmethod | |
| def c(cls) -> None: ... | |
| @property | |
| def p(self) -> int: | |
| return 0 | |
| @property | |
| def p2(self) -> int: | |
| return 0 | |
| @p2.setter | |
| def p2(self, x: int) -> None: | |
| pass | |
| def f1() -> int: | |
| c = C() | |
| c.s() | |
| c.c() | |
| c.p2 = 1 | |
| return c.p + c.p2 | |
| class A(ABC): | |
| @abstractmethod | |
| def m(self) -> int: | |
| raise NotImplementedError # A: Get non-native attribute "NotImplementedError". | |
| class D(A): | |
| def m(self) -> int: | |
| return 1 | |
| def f2() -> int: | |
| d = D() | |
| return d.m() |