DF
David Fisher 9 years ago
This is probably a confluence of PartialTypes and context. Yikes.
- Mypy strict-optional issues
- Catalog of issues
- Issues in type-checker
- Pseudo-instance attributes declared on class
- NodeVisitor — None / Generic / subclassing
- parse_signature — Tuple and empty container
- Lexer and many others — Callable rejected at own type
- deserialize — if/else becomes object and fails to match Optional[Thing]
- cryptic error message on attribute use
- any_constraints — lacking conditional type binder in comprehension
- as_block — type update due to assignment
- really need to print whole types — tuple edition
- subtype on Callable neglecting optionality of args
- list slices should be effectively covariant
- wrap_context — yield value expected
- Issues in mypy as annotated codebase
- parse_docstring — handing over a Dict, wishing it were covariant in value type
- waiter — inferring an Optional local
- infer_constraints_for_callable — conditional type binder doesn’t apply to dict items
- Issues in typeshed stubs
- Data
Catalog of issues
None / Generic / subclassing #1956Issues in type-checker
Pseudo-instance attributes declared on class
class C:
x = None # type: str
def __init__(self, x: str = None) -> None:
self.x = x
class C:
x = None # type: str
def __init__(self) -> None:
pass
def run(self) -> None:
print('x: ' + self.x)
NodeVisitor — None / Generic / subclassing
class NodeVisitor(Generic[T]):
...
def visit_mypy_file(self, o: 'mypy.nodes.MypyFile') -> T:
pass
...
class TraverserVisitor(NodeVisitor[None]):
...
def visit_mypy_file(self, o: MypyFile) -> None:
for d in o.defs:
d.accept(self)
$ cat /tmp/foo.py
from typing import Generic, TypeVar
T = TypeVar('T')
class Base(Generic[T]):
def f(self) -> T:
pass
class SubNone(Base[None]):
def f(self) -> None:
pass
class SubInt(Base[int]):
def f(self) -> int:
return 1
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py: note: In class "SubNone":
/tmp/foo.py:10: error: Return type of "f" incompatible with supertype "Base"
parse_signature — Tuple and empty container
mypy/stubutil.py: note: In function "parse_signature":
mypy/stubutil.py:21: error: Incompatible return value type (got tuple(length 3), expected "Optional[Tuple[str, List[str], List[str]]]")
def parse_signature(sig: str) -> Optional[Tuple[str,
List[str],
List[str]]]:
...
if not arg_string.strip():
return (name, [], [])
$ cat /tmp/foo.py
from typing import Optional, Tuple, List
def f1() -> Optional[Tuple[List[str]]]:
return ([],)
def f2() -> Optional[Tuple[List[str]]]:
return ([''],)
def f3() -> Optional[List[str]]:
return []
$ mypy /tmp/foo.py
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py: note: In function "f1":
/tmp/foo.py:4: error: Incompatible return value type (got "Tuple[List[object]]", expected "Optional[Tuple[List[str]]]")
Lexer and many others — Callable rejected at own type
mypy/lex.py: note: In member "__init__" of class "Lexer":
mypy/lex.py:328: error: Incompatible types in assignment (expression has type Callable[[], None], target has type Callable[[], None])
$ cat /tmp/foo.py
from typing import Callable
def f() -> None:
...
x = f # type: Callable[[], None]
reveal_type(f)
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:6: error: Incompatible types in assignment (expression has type Callable[[], None], variable has type Callable[[], None])
/tmp/foo.py:7: error: Revealed type is 'def ()'
$ mypy /tmp/foo.py
/tmp/foo.py:7: error: Revealed type is 'def ()'
mypy/lex.py: note: In member "lex" of class "Lexer":
mypy/lex.py:378: error: Argument 2 to "get" of "dict" has incompatible type Callable[[], None]; expected "Optional[Callable[[], None]]"
$ cat /tmp/foo.py
from typing import Callable
def f() -> None:
...
d1 = {} # type: Dict[int, Callable[[], None]]
d1.get(1, lambda: None)
d1.get(1, f)
d2 = {} # type: Dict[int, str]
d2.get(1, 'a')
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:8: error: Argument 2 to "get" of "dict" has incompatible type Callable[[], None]; expected "Optional[Callable[[], None]]"
mypy/checkexpr.py: note: In member "check_argument_types" of class "ExpressionChecker":
mypy/checkexpr.py:658: error: Incompatible types in assignment (expression has type union type (2 items), variable has type "Optional[Callable[[Type, Type, int, Type, int, int, CallableType, Context, MessageBuilder], None]]")
mypy/checkexpr.py:677: error: None not callable
mypy/checkexpr.py:691: error: None not callable
...
check_arg: ArgChecker = None) -> None:
...
check_arg = check_arg or self.check_arg
...
check_arg(actual_type, arg_type, arg_kinds[actual],
callee.arg_types[i],
actual + 1, i + 1, callee, context, messages)
deserialize — if/else becomes object and fails to match Optional[Thing]
mypy/nodes.py: note: In member "deserialize" of class "Argument":
mypy/nodes.py:402: error: Argument 2 to "Argument" has incompatible type "object"; expected "Optional[Type]"
return Argument(Var.deserialize(data['variable']),
(None if data.get('type_annotation') is None
else mypy.types.Type.deserialize(data['type_annotation'])),
$ cat /tmp/foo.py
from typing import Optional
x = None if 3 else 3 # type: Optional[int]
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:3: error: Incompatible types in assignment (expression has type "object", variable has type "Optional[int]")
mypy/types.py: note: In member "deserialize" of class "CallableType":
mypy/types.py:656: error: List comprehension has incompatible type List[object]
@classmethod
def deserialize(cls, data: JsonDict) -> 'CallableType':
assert data['.class'] == 'CallableType'
# TODO: Set definition to the containing SymbolNode?
return CallableType([(None if t is None else Type.deserialize(t))
for t in data['arg_types']],
cryptic error message on attribute use
mypy/nodes.py: note: In member "serialize" of class "SymbolTableNode":
mypy/nodes.py:2086: error: Some element of union has no attribute "fullname"
data['cross_ref'] = self.node.fullname()
$ cat /tmp/foo.py
from typing import Optional
x = 'abc' # type: Optional[str]
x.index('b')
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:4: error: Some element of union has no attribute "index"
any_constraints — lacking conditional type binder in comprehension
mypy/constraints.py: note: In function "any_constraints":
mypy/constraints.py:187: error: Incompatible return value type (got "Optional[List[Constraint]]", expected List[Constraint])
def any_constraints(options: List[Optional[List[Constraint]]]) -> List[Constraint]:
...
valid_options = [option for option in options if option is not None]
if len(valid_options) == 1:
return valid_options[0]
...
$ cat /tmp/foo.py
from typing import Optional, List
l = [1, None] # type: List[Optional[int]]
ll = [x for x in l if x is not None] # type: List[int]
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:4: error: List comprehension has incompatible type List[Optional[int]]
as_block — type update due to assignment
mypy/fastparse.py: note: In member "as_block" of class "ASTConverter":
mypy/fastparse.py:185: error: Some element of union has no attribute "set_line"
def as_block(self, stmts: List[ast35.stmt], lineno: int) -> Block:
b = None
if stmts:
b = Block(self.fix_function_overloads(self.visit_list(stmts)))
b.set_line(lineno)
return b
$ cat /tmp/foo.py
x = None
if 32:
x = 'abc'
x + x
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:4: error: Unsupported left operand type for + (some union)
$ cat /tmp/foo.py
x = None
if 32:
x = 'abc'
if x is not None:
x + x
$ mypy /tmp/foo.py --strict-optional
$ cat /tmp/foo.py
x = None
x = 'abc'
x + x
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:3: error: Unsupported left operand type for + (some union)
really need to print whole types — tuple edition
mypy/parse.py: note: In member "parse_function_header" of class "Parser":
mypy/parse.py:550: error: Incompatible return value type (got tuple(length 5), expected tuple(length 5))
return (name, [], None, True, [])
subtype on Callable neglecting optionality of args
mypy/semanal.py: note: In member "anal_type" of class "SemanticAnalyzer":
mypy/semanal.py:1048: error: Argument 3 to "TypeAnalyser" has incompatible type Callable[[str, Context, bool, bool], None]; expected Callable[[str, Context], None]
a = TypeAnalyser(self.lookup_qualified,
self.lookup_fully_qualified,
self.fail)
...
def fail(self, msg: str, ctx: Context, serious: bool = False, *,
blocker: bool = False) -> None:
$ cat /tmp/foo.py
from typing import Callable
def f(x: str, y: bool = False, *, z: bool = False) -> None:
...
f('a')
ff = f # type: Callable[[str], None]
$ mypy /tmp/foo.py
$ mypy /tmp/foo.py --strict-optional
/tmp/foo.py:7: error: Incompatible types in assignment (expression has type Callable[[str, bool, bool], None], variable has type Callable[[str], None])
list slices should be effectively covariant
mypy/semanal.py: note: In member "process_typevar_declaration" of class "SemanticAnalyzer":
mypy/semanal.py:1314: error: Argument 2 to "process_typevar_parameters" of "SemanticAnalyzer" has incompatible type List[str]; expected List[Optional[str]]
res = self.process_typevar_parameters(call.args[1 + n_values:],
call.arg_names[1 + n_values:],
wrap_context — yield value expected
mypy/build.py: note: In member "wrap_context" of class "State":
mypy/build.py:1170: error: Yield value expected
@contextlib.contextmanager
def wrap_context(self) -> Iterator[None]:
save_import_context = self.manager.errors.import_context()
self.manager.errors.set_import_context(self.import_context)
try:
yield
except CompileError:
raise
except Exception as err:
report_internal_error(err, self.path, 0)
self.manager.errors.set_import_context(save_import_context)
self.check_blockers()
Issues in mypy as annotated codebase
parse_docstring — handing over a Dict, wishing it were covariant in value type
mypy/docstring.py: note: In function "parse_docstring":
mypy/docstring.py:189: error: Incompatible types in assignment (expression has type Dict[str, str], variable has type Dict[str, Optional[str]])
waiter — inferring an Optional local
mypy/waiter.py: note: In member "_wait_next" of class "Waiter":
mypy/waiter.py:209: error: Incompatible types in assignment (expression has type None, variable has type "str")
if rc != 0:
if name not in self.xfail:
fail_type = 'FAILURE'
else:
fail_type = 'XFAIL'
else:
if name not in self.xfail:
fail_type = None
else:
fail_type = 'UPASS'
infer_constraints_for_callable — conditional type binder doesn’t apply to dict items
mypy/constraints.py: note: In function "infer_constraints_for_callable":
mypy/constraints.py:57: error: Argument 1 to "get_actual_type" has incompatible type "Optional[Type]"; expected "Type"
if arg_types[actual] is None:
continue
actual_type = get_actual_type(arg_types[actual], arg_kinds[actual],
tuple_counter)
Issues in typeshed stubs
mypy/waiter.py: note: In member "start" of class "LazySubprocess":
mypy/waiter.py:36: error: Argument 2 to "Popen" has incompatible type "Optional[str]"; expected "str"
mypy/waiter.py:36: error: Argument 3 to "Popen" has incompatible type "Optional[Dict[str, str]]"; expected Mapping[str, str]
self.process = Popen(self.args, cwd=self.cwd, env=self.env,
stdout=self.outfile, stderr=STDOUT)
Data