def hello(text: str) -> str:
return 'Hello, {0}'.format(text)
def sum(a: int, b: int) -> int:
return a + b
from decimal import Decimal, ROUND_UP
PRECISION = Decimal('.01')
def quantize_payment(hours: float, rate: Decimal) -> Decimal:
return (Decimal(hours) * rate).quantize(PRECISION, ROUND_UP)
from typing import AnyStr, Text
def response(body: bytes=None, text: str=None) -> bytes:
...
def response(content: AnyStr=None) -> bytes:
...
def render_template(path: Text) -> str:
...
from typing import List, Sequence, Set, Tuple
def avg(data: list) -> float:
return sum(data, 0.) / len(data)
def avg(data: List[float]) -> float:
return sum(data, 0.) / len(data)
def split_name(value: str) -> Tuple[str, str]:
return tuple(value.split(' ', 1))
def avg_tuple(data: Tuple[float, ...]) -> float:
return sum(data, 0.) / len(data)
def unique(data: List[str]) -> Set[str]:
return set(data)
def avg(data: Sequence[float]) -> float:
return sum(data, 0.) / len(data)
from typing import Dict, Mapping
def read_data(slug: str) -> dict:
...
def read_data(slug: str) -> Dict[str, str]:
...
def process_data(data: Mapping[str, str]) -> None:
for key, value in data.items():
...
from typing import Any, Optional, Sequence, Union
def read_data(slug: str) -> Dict[str, Union[int, str]]:
...
def read_data(slug: str) -> Dict[str, Any]:
...
def avg(data: Sequence[float]) -> Optional[float]:
if len(data):
return sum(data, 0.) / len(data)
return None
from random import randint
from typing import Callable
def random_int_factory(min: int=0, max: int=100) -> Callable[[], int]:
def random_int() -> int:
return randint(min, max)
return random_int
from aiohttp import web
View = Callable[[web.Request], web.Response]
def middleware() -> Callable[[web.Application, View], Awaitable[View]]:
async def factory(app: web.Application, handler: View) -> View:
async def middleware(request: web.Request) -> web.Response:
...
class Schema(object):
name = None # type: str
def __init__(self, name: str=None) -> None:
self.name = name or self.name
def clone(self) -> 'Schema':
return Schema(self.name)
def load(self) -> bool:
with open(SCHEMA_PATH / '{0}.json'.format(self.name)) as handler:
return json.loads(handler.read())
def validate(self, data: Dict[str, Any]) -> Dict[str, Any]:
json_schema = self.load()
return fastjsonschema.compile(json_schema).validate(data)
from typing import NewType
UserID = NewType('UserID', int)
def fetch_user(user_id: UserID) -> Any:
...
fetch_user(UserID(1)) # OK
fetch_user(1) # Type check will fail
WEEKS = defaultdict(lambda: {
False: list(range(8)),
True: list(range(16)),
}) # type: Dict[str, Dict[bool, List[int]]]
package/module.py
def avg(data):
if not len(data):
return None
return sum(data, 0.) / len(data)
def avg_unique(data):
return avg(unique(data))
def unique(data):
return set(data)
package/module.pyi
def avg(data: Sequence[float]) -> Optional[float]: ...
def avg_unique(data: Sequence[float]) -> Optional[float]: ...
def unique(data: Sequence[float]) -> Set[float]: ...
def hello(name): # type: (name: str) -> str
return 'Hello, {0}'.format(name)
def multi_line_annotations(address, # type: Union[str, List[str]]
sender, # type: str
subject, # type: str
body # type: str
):
# type: (...) -> bool
...
More info at mypy docs.
pip install mypy-lang typed-ast
mypy ...
mypy --fast-parser ...
mypy.ini
[mypy]
fast_parser = True
check_untyped_defs = True
warn_redundant_casts = True
mypy.ini
[mypy]
fast_parser = True
silent_imports = True
check_untyped_defs = True
warn_redundant_casts = True
mypy.ini
[mypy]
fast_parser = True
silent_imports = True
check_untyped_defs = True
disallow_untyped_defs = True
warn_redundant_casts = True
$ mypy project/
project/signals.py: note: In function "cache_api_urls":
project/signals.py:25: error: Argument 2 to "api_url" has incompatible type "str"; expected "int"
mypy
is not widely usedBefore
async def retrieve_tweets(pool, count=50):
...
After
async def retrieve_tweets(
pool: Pool,
count: int=50
) -> Sequence[Mapping[str, Any]]:
...
import asyncio
def main() -> int:
loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(some_async_def),
asyncio.ensure_future(some_other_async_def),
]
loop.run_until_complete(asyncio.gather(*tasks, loop=loop))
loop.close()
return 0
mypy -c '...'
<string>: note: In function "main":
<string>:6: error: "module" has no attribute "ensure_future"
<string>:6: error: Name 'some_async_def' is not defined
<string>:7: error: "module" has no attribute "ensure_future"
<string>:7: error: Name 'some_other_async_def' is not defined
<string>:9: error: "module" has no attribute "gather"
from lxml import etree
def fetch_data(url: str) -> etree._Element:
...
def use_data():
data = fetch_data(URL)
for item in data.iterfind(...): # Will fail with "has no attribute" error
...
# noqa: F401
from typing import Dict, List, Set
def process_data(data: Dict[str, str]) -> List[int]:
uniques = set() # type: Set[int]
for value in data.values():
uniques.add(int(value))
return list(uniques)
module.py:1:1: F401 'typing.Set' imported but unused
# noqa: F401
PEP-526 implements syntax for variable annotations.
uniques: Set[int] = set()
number: int # Works even without assignment
class Schema(object):
name: str
data: Dict[str, str] = {}
Included in Python 3.6
project/models.py
from .utils import some_func
class Model(object):
def shortcut_for_some_func(self) -> int:
return some_func(self)
project/utils.py
import typing
if typing.TYPE_CHECKING:
from .models import Model
def some_func(model: 'Model') -> int:
...
# type: ignore
Sooner or later, but you'll need to use # type: ingore
asyncio.gather(*tasks, loop=loop) # type: ignore
for item in data.iterfind('...'): # type: ignore
mypy is still experimental and you'll need to use
# type: ignore
after yelling WTF 😔
import enforce
@enforce.runtime_validation
def hello(name: str) -> str:
return 'Hello, {0}!'.format(name)
hello('world')
hello(1) # Will fail with RuntimeTypeError