by Bernat Gabor /
@gjbernat /
Bloomberg
https://gaborbernat.github.io/pycon-us-2019
from typing import Any
def send_request(request_data : Any,
headers: Optional[Dict[str, str]],
user_id: Optional[UserId] = None,
as_json: bool = True):
pass
def send_request(request_data : Any,
headers: Optional[Dict[str, str]],
user_id: Optional[UserId] = None,
as_json: bool = True):
pass
def send_request(request_data : Any,
headers: Optional[Dict[str, str]],
user_id: Optional[UserId] = None,
as_json: bool = True):
pass
def send_request(request_data : Any,
headers: Optional[Dict[str, str]],
user_id: Optional[UserId] = None,
as_json: bool = True):
pass
def send_request(request_data : Any,
headers: Optional[Dict[str, str]],
user_id: Optional[UserId] = None,
as_json: bool = True):
pass
from datetime import datetime
from typing import List
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: datetime = None
friends: List[int] = []
external_data = {'id': '123', 'signup_ts': 'broken',
'friends': [1, 2, 'not a number']}
try:
user = User(**external_data)
except ValidationError as e:
print(e.json())
Facebook - pyre
def magic_call(unsafe: UserProvidedStr, safe: str):
subprocess.check_call([unsafe])
subprocess.check_call([safe])
Facebook - pyre
def magic_call(unsafe: UserProvidedStr, safe: str):
subprocess.check_call([unsafe]) # error out
subprocess.check_call([safe])
Facebook - pyre
def magic_call(unsafe: UserProvidedStr, safe: str):
subprocess.check_call([unsafe])
subprocess.check_call([safe]) # allow
from datetime import datetime
from typing import List
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: datetime = None
friends: List[int] = []
external_data = {'id': '123', 'signup_ts': 'broken',
'friends': [1, 2, 'not a number']}
try:
user = User(**external_data)
except ValidationError as e:
print(e.json())
from datetime import datetime
from typing import List
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: datetime = None
friends: List[int] = []
external_data = {'id': '123', 'signup_ts': 'broken',
'friends': [1, 2, 'not a number']}
try:
user = User(**external_data)
except ValidationError as e:
print(e.json())
essentially treated as comment during script evaluation
essentially treated as comment during script evaluation
Improve the developer experience, not performance
Improve the developer experience, not performance
Improve the developer experience, and performance
# tests/test_magic_field.py
f = MagicField(name=1, MagicType.DEFAULT)
f.names()
bernat@uvm ~/python-magic (master●)$ mypy --ignore-missing-imports tests/test_magic_field.py
tests/test_magic_field.py:21: error: Argument 1 to "MagicField" has incompatible type "int";
expected "Union[str, bytes]"
tests/test_magic_field.py:22: error: "MagicField" has no attribute "names"; maybe "name" or "_name"?
# tests/test_magic_field.py
f = MagicField(name=1, MagicType.DEFAULT)
f.names()
bernat@uvm ~/python-magic (master●)$ mypy --ignore-missing-imports tests/test_magic_field.py
tests/test_magic_field.py:21: error: Argument 1 to "MagicField" has incompatible type "int";
expected "Union[str, bytes]"
tests/test_magic_field.py:22: error: "MagicField" has no attribute "names"; maybe "name" or "_name"?
# tests/test_magic_field.py
f = MagicField(1, MagicType.DEFAULT)
f.names()
bernat@uvm ~/python-magic (master●)$ mypy --ignore-missing-imports tests/test_magic_field.py
tests/test_magic_field.py:21: error: Argument 1 to "MagicField" has incompatible type "int";
expected "Union[str, bytes]"
tests/test_magic_field.py:22: error: "MagicField" has no attribute "names"; maybe "name" or "_name"?
# tests/test_magic_field.py
f = MagicField(1, MagicType.DEFAULT)
f.names()
bernat@uvm ~/python-magic (master●)$ mypy --ignore-missing-imports tests/test_magic_field.py
tests/test_magic_field.py:21: error: Argument 1 to "MagicField" has incompatible type "int";
expected "Union[str, bytes]"
tests/test_magic_field.py:22: error: "MagicField" has no attribute "names"; maybe "name" or "_name"?
def greeting(name: str) -> str:
value : str = 'Hello'
return value + name
def greeting(name: str) -> str:
value : str = 'Hello'
return value + name
def greeting(name: str) -> str:
value : str = 'Hello'
return value + name
from typing import List
class A(object):
def __init__() -> None:
self.elements : List[int] = []
def add(element: int) -> None:
self.elements.append(element)
from __future__ import annotations
from typing import List
class A(object):
def __init__():
# type: () -> None
self.elements = [] # type: List[int]
def add(element):
# type: (List[int]) -> None
self.elements.append(element)
@contextmanager
def swap_in_state(state, config, overrides):
old_config, old_overrides = state.config, state.overrides
state.config, state.overrides = config, overrides
yield old_config, old_overrides
state.config, state.overrides = old_config, old_overrides
@contextmanager
def swap_in_state(state, # type: State
config, # type: HasGetSetMutable
overrides # type: Optional[HasGetSetMutable]
):
# type: (...) -> Generator[Tuple[HasGetSetMutable, Optional[HasGetSetMutable]], None, None]
old_config, old_overrides = state.config, state.overrides
state.config, state.overrides = config, overrides
yield old_config, old_overrides
state.config, state.overrides = old_config, old_overrides
from typing import Generator, Tuple, Optional
from magic import RunSate, HasGetSetMutable
@contextmanager
def swap_in_state(state, # type: State
config, # type: HasGetSetMutable
overrides # type: Optional[HasGetSetMutable]
):
# type: (...) -> Generator[Tuple[HasGetSetMutable, Optional[HasGetSetMutable]], None, None]
old_config, old_overrides = state.config, state.overrides
state.config, state.overrides = config, overrides
yield old_config, old_overrides
state.config, state.overrides = old_config, old_overrides
from typing import Generator, Tuple, Optional, Dict, List
from magic import RunSate
HasGetSetMutable = Union[Dict, List] # pylint: disable=invalid-name
@contextmanager
def swap_in_state(state, # type: State
config, # type: HasGetSetMutable
overrides # type: Optional[HasGetSetMutable]
): # pylint: disable=bad-continuation
# type: (...) -> Generator[Tuple[HasGetSetMutable, Optional[HasGetSetMutable]], None, None]
old_config, old_overrides = state.config, state.overrides
state.config, state.overrides = config, overrides
yield old_config, old_overrides
state.config, state.overrides = old_config, old_overrides
@contextmanager
def swap_in_state(state, config, overrides):
old_config, old_overrides = state.config, state.overrides
state.config, state.overrides = config, overrides
yield old_config, old_overrides
state.config, state.overrides = old_config, old_overrides
from typing import Generator, Tuple, Optional, Dict, List
from magic import RunSate
HasGetSetMutable = Union[Dict, List] # pylint: disable=invalid-name
Result = Generator[ # pylint: disable=invalid-name
Tuple[HasGetSetMutable, Optional[HasGetSetMutable]], None, None]
@contextmanager
def config_in_state(state, # type: State
config, # type: HasGetSetMutable
overrides # type: Optional[HasGetSetMutable]
): # pylint: disable=bad-continuation
# type: (...) -> Result
old_config, old_overrides = state.config, state.overrides
state.config, state.overrides = config, overrides
yield old_config, old_overrides
state.config, state.overrides = old_config, old_overrides
class A(object):
def __init__() -> None:
self.elements = []
def add(element):
self.elements.append(element)
# a.pyi alongside a.py
from typing import List
class A(object):
elements = ... # type: List[int]
def __init__() -> None: ...
def add(element: int) -> None: ...
class A(object):
def __init__() -> None:
self.elements = []
def add(element):
self.elements.append(element)
# a.pyi alongside a.py - ellipse for body stub
from typing import List
class A(object):
elements = ... # type: List[int]
def __init__() -> None: ...
class A(object):
def __init__():
self.elements = []
def add(element):
"""
:param List[int] element: the element to add
:rtype: None
"""
self.elements.append(element)
t : Tuple[int, float] = 0, 1.2
d : Dict[str, int] = {"a": 1, "b": 2}
d : MutableMapping[str, int] = {"a": 1, "b": 2}
l : List[int] = [1, 2, 3]
i : Iterable[Text] = [ u'1', u'2', u'3']
Vector = List[float]
UserId = NewType('UserId', int)
some_id = UserId(524313)
class Employee(NamedTuple):
name: str
id: int
Union[None, int, str] # one of
Optional[float] # Union[None, float]
# Callable[[Arg1Type, Arg2Type], ReturnType]
def feeder(get_next_item: Callable[[], str]) -> None:
T = TypeVar('T')
class Magic(Generic[T]):
def __init__(self, value: T) -> None:
self.value : T = value
def square_values(vars: Iterable[Magic[int]]) -> None:
v.value = v.value * v.value
def foo(item: Any) -> int:
item.bar()
nominal (main) vs structural typing (support)
KEY = TypeVar('KEY', contravariant=true)
class MagicGetter(Protocol[KEY], Sized):
def __getitem__(self, item: KEY) -> int: ...
def func_int(param: MagicGetter[int]) -> int:
return param['a'] * 2
def func_str(param: MagicGetter[str]) -> str:
return '{}'.format(param['a'])
nominal (main) vs structural typing (support)
KEY = TypeVar('KEY', contravariant=true)
class MagicGetter(Protocol[KEY], Sized):
def __getitem__(self, item: KEY) -> int: ...
def func_int(param: MagicGetter[int]) -> int:
return param['a'] * 2
def func_str(param: MagicGetter[str]) -> str:
return '{}'.format(param['a'])
class A(object):
def __repr__(self):
# type: () -> str
return 'Rule({})'.format(self.full_name)
from __future__ import unicode_literals
class A(object):
def __repr__(self):
# type: () -> str
res = 'Rule({})'.format(self.full_name)
if sys.version_info > (3, 0):
# noinspection PyTypeChecker
return res
# noinspection PyTypeChecker
return res.encode('utf-8')
def magic(i: Union[str, int]) -> Union[str, int]:
return i * 2
def other_func() -> int:
result = magic(2)
assert isinstance(result, int)
return result
def magic(i: Union[str, int]) -> Any:
return i * 2
def other_func() -> int:
result = magic(2)
return result
from typing import overload
@overload
def magic(i: int) -> int:
pass
@overload
def magic(i: str) -> str:
pass
def magic(i: Union[int, str]) -> Union[int, str]:
return i * 2
def other_func() -> int:
result = magic(2)
return result
from typing import overload
@overload
def magic(i: int) -> int: # pylint: disable=function-redefined
pass
@overload
def magic(i: str) -> str: # pylint: disable=function-redefined
pass
def magic(i: Union[int, str]) -> Union[int, str]:
return i * 2
def other_func() -> int:
result = magic(2)
return result
class A(object):
def float(self):
# type: () -> float
return 1.0
test.py:3: error: Invalid type "test.A.float"
look for type in the closest namespace - 3775
if typing.TYPE_CHECKING:
import builtins
class A(object):
def float(self):
# type: () -> builtins.float
return 1.0
test.py:3: error: Invalid type "test.A.float"
look for type in the closest namespace - 3775
from abc import ABCMeta, abstractmethod
from typing import Union
class A(metaclass=ABCMeta):
@abstractmethod
def func(self, key): # type: (Union[int, str]) -> str
raise NotImplementedError
class B(A):
def func(self, key): # type: (int) -> str
return str(key)
class C(A):
def func(self, key): # type: (str) -> str
return key
test.py:12: error: Argument 1 of "func" incompatible with supertype "A"
test.py:17: error: Argument 1 of "func" incompatible with supertype "A"
specialization can handle more
from abc import ABCMeta, abstractmethod
from typing import Union
class A(metaclass=ABCMeta):
@abstractmethod
def func(self, key): # type: (Union[int, str]) -> str
raise NotImplementedError
class B(A):
def func(self, key): # type: (Union[int, str, bool]) -> str
return str(key)
class C(A):
def func(self, key): # type: (Union[int, str, List]) -> str
return key
class A:
@classmethod
def magic(cls, a: int) -> 'A':
return cls()
class B(A):
@classmethod
def magic(cls, a: int, b: bool) -> 'B':
return cls()
from typing import List, Type
elements : List[Type[A]] = [A, B]
print( [e.magic(1) for e in elements])
print( [e.magic(1) for e in elements])
TypeError: magic() missing 1 required positional argument: 'b'
test.py:9: error: Signature of "magic" incompatible with supertype "A"
class A:
@classmethod
def magic(cls, a: int) -> 'A':
return cls()
class B(A):
@classmethod
def magic(cls, a: int, b: bool = False) -> 'B':
return cls()
from typing import List, Type
elements : List[Type[A]] = [A, B]
print( [e.magic(1) for e in elements])
class A:
def __init__(self, a: int) -> None:
pass
class B(A):
def __init__(self, a: int, b: bool) -> None:
super().__init__(a)
from typing import List, Type
elements : List[Type[A]]= [A, B]
print( [e(1) for e in elements])
print( [e(1) for e in elements])
TypeError: __init__() missing 1 required positional argument: 'b'
from typing import List, cast
a = [4]
reveal_type(a) # -> error: Revealed type is 'builtins.list[builtins.int*]'
b = cast(List[int], a) # passes fine
c = cast(List[str], a) # type: List[str] # passes fine (no runtime check)
reveal_type(c) # -> error: Revealed type is 'builtins.list[builtins.str]'
x = confusing_function() # type: ignore # see mypy/issues/1167
class A(object):
def __init__():
self.elements = []
def add(element):
"""
:param List[int] element: the element to add
:rtype: None
"""
self.elements.append(element)
pip install sphinx-autodoc-types>=2.1.1
# conf.py
extensions = ['sphinx_autodoc_typehints']
@overload
def open(fn: str, mode: Literal['r', 'w']) -> IO[Text]: pass
@overload
def open(fn: str, mode: Literal['rb', 'wb']) -> IO[bytes]: pass
def open(fn: str, mode: str):
...
with open("/etc/passwd", "r") as ft:
lines = ft.readlines() # List[Text]
with open("/bin/sh", "rb") as fb:
data = fb.read(512) # bytes
@overload
def open(fn: str, mode: Literal['r', 'w']) -> IO[Text]: pass
@overload
def open(fn: str, mode: Literal['rb', 'wb']) -> IO[bytes]: pass
def open(fn: str, mode: str):
...
with open("/etc/passwd", "r") as ft:
lines = ft.readlines() # List[Text]
with open("/bin/sh", "rb") as fb:
data = fb.read(512) # bytes
@overload
def open(fn: str, mode: Literal['r', 'w']) -> IO[Text]: pass
@overload
def open(fn: str, mode: Literal['rb', 'wb']) -> IO[bytes]: pass
def open(fn: str, mode: str):
...
with open("/etc/passwd", "r") as ft:
lines = ft.readlines() # List[Text]
with open("/bin/sh", "rb") as fb:
data = fb.read(512) # bytes
class Movie(TypedDict):
name: str
year: int
movie: Movie = {'name': 'Blade Runner', 'year': 1982}
RATE: Final = 3000
RATE = 2500 # ERROR -- cannot set Final variable
# Final class variable
class User:
DEFAULT_ID: Final = 0
class BusinessUser(User):
DEFAULT_ID = 1 # ERROR -- cannot set inherited Final class variable
RATE: Final = 3000
RATE = 2500 # ERROR -- cannot set Final variable
# Final class variable
class User:
DEFAULT_ID: Final = 0
class BusinessUser(User):
DEFAULT_ID = 1 # ERROR -- cannot set inherited Final class variable
RATE: Final = 3000
RATE = 2500 # ERROR -- cannot set Final variable
# Final class variable
class User:
DEFAULT_ID: Final = 0
class BusinessUser(User):
DEFAULT_ID = 1 # ERROR -- cannot set inherited Final class variable
from typing import List
a: List[int] = [1, 2]
b : Dict[int, str] = {}
a: list[int] = [1, 2]
b : dict[int, str] = {}
a : Union[str, int]
b : Optional[int] = {}
a : str | int
b : ?int = {}
Bloomberg
© 2019 Bloomberg Finance L.P.
All rights reserved.