Skip to content

Commit f6cd1d6

Browse files
committed
Merge branch 'optional-types'
Closes #477.
2 parents 59ca853 + b051827 commit f6cd1d6

File tree

12 files changed

+85
-8
lines changed

12 files changed

+85
-8
lines changed

lib-typing/2.7/test_typing.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from typing import (
66
List, Dict, Set, Tuple, Pattern, Match, Any, Callable, Generic,
7-
_Protocol, Sized, Iterable, Iterator, Sequence,
7+
_Protocol, Sized, Iterable, Iterator, Sequence, Union, Optional,
88
AbstractSet, Mapping, BinaryIO, TextIO, SupportsInt, SupportsFloat,
99
SupportsAbs, Reversible, Undefined, AnyStr, annotations, builtinclass,
1010
cast, disjointclass, ducktype, forwardref, overload, typevar
@@ -455,6 +455,11 @@ def h(x): pass
455455
@overload
456456
def h(x): pass
457457

458+
def test_optional(self):
459+
# TODO: This test actually isn't very useful right now, but it will make sense
460+
# once Union is modified to keep track of the given type arguments.
461+
self.assertEqual(Optional[int], Union[int, None])
462+
458463

459464
class Dummy(object):
460465
u"""Dummy class defined in module scope"""

lib-typing/2.7/typing.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'List',
2121
'Match',
2222
'NamedTuple',
23+
'Optional',
2324
'Pattern',
2425
'Set',
2526
'Tuple',
@@ -164,9 +165,18 @@ def __getitem__(self, typeargs):
164165

165166
def union(x): return x
166167

168+
167169
Union = TypeAlias(union)
168170

169171

172+
class _Optional:
173+
def __getitem__(self, typearg):
174+
return Union[typearg, None]
175+
176+
177+
Optional = _Optional()
178+
179+
170180
def NamedTuple(typename, fields):
171181
return collections.namedtuple(typename,
172182
(name for name, type in fields))

lib-typing/3.2/test_typing.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from typing import (
55
List, Dict, Set, Tuple, Pattern, Match, Any, Callable, Generic,
6-
_Protocol, Sized, Iterable, Iterator, Sequence,
6+
_Protocol, Sized, Iterable, Iterator, Sequence, Union, Optional,
77
AbstractSet, Mapping, BinaryIO, TextIO, SupportsInt, SupportsFloat,
88
SupportsAbs, SupportsRound, Reversible, Undefined, AnyStr, builtinclass,
99
cast, disjointclass, ducktype, forwardref, overload, typevar
@@ -460,6 +460,11 @@ def h(x: int) -> None: pass
460460
@overload
461461
def h(x: str) -> None: pass
462462

463+
def test_optional(self):
464+
# TODO: This test actually isn't very useful right now, but it will make sense
465+
# once Union is modified to keep track of the given type arguments.
466+
self.assertEqual(Optional[int], Union[int, None])
467+
463468

464469
class Dummy:
465470
"""Dummy class defined in module scope"""

lib-typing/3.2/typing.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'List',
2121
'Match',
2222
'NamedTuple',
23+
'Optional',
2324
'Pattern',
2425
'Set',
2526
'Tuple',
@@ -168,6 +169,14 @@ def union(x): return x
168169
Union = TypeAlias(union)
169170

170171

172+
class _Optional:
173+
def __getitem__(self, typearg):
174+
return Union[typearg, None]
175+
176+
177+
Optional = _Optional()
178+
179+
171180
def NamedTuple(typename, fields):
172181
return collections.namedtuple(typename,
173182
(name for name, type in fields))

mypy/test/data/check-unions.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,10 @@ from typing import Union
103103
def f() -> Union[int, None]: pass
104104
x = 1
105105
x = f()
106+
107+
[case testOptional]
108+
from typing import Optional
109+
def f(x: Optional[int]) -> None: pass
110+
f(1)
111+
f(None)
112+
f('') # E: Argument 1 to "f" has incompatible type "str"; expected "int"

mypy/test/data/lib-stub/typing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
Undefined = object()
99
Any = object()
1010
Union = object()
11+
Optional = object()
1112
typevar = object()
1213
Generic = object()
1314
Tuple = object()

mypy/test/data/python2eval.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,11 @@ f([0, 0, 1, 1, 1])
350350
[out]
351351
2
352352
3
353+
354+
[case testOptional_python2]
355+
from typing import Optional
356+
def f(): # type: () -> Optional[int]
357+
pass
358+
x = f()
359+
y = 1
360+
y = x

mypy/test/data/pythoneval.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,3 +986,10 @@ class E(A, B[T], Generic[T]): pass
986986
class F(B[T], A, Generic[T]): pass
987987
def f(e: E[int], f: F[int]) -> None: pass
988988
[out]
989+
990+
[case testOptional]
991+
from typing import Optional
992+
def f() -> Optional[int]: pass
993+
x = f()
994+
y = 1
995+
y = x

mypy/test/data/semanal-types.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,3 +1522,20 @@ MypyFile:1(
15221522
def (x: builtins.int)
15231523
Block:2(
15241524
PassStmt:2())))
1525+
1526+
[case testOptionalTypes]
1527+
from typing import Optional
1528+
x = 1 # type: Optional[int]
1529+
[out]
1530+
MypyFile:1(
1531+
ImportFrom:1(typing, [Optional : Optional])
1532+
AssignmentStmt:2(
1533+
NameExpr(x [__main__.x])
1534+
IntExpr(1)
1535+
builtins.int))
1536+
1537+
[case testInvalidOptionalType]
1538+
from typing import Optional
1539+
x = 1 # type: Optional[int, str] # E: Optional[...] must have exactly one type argument
1540+
y = 1 # type: Optional[x] # E: Invalid type "__main__.x"
1541+
[out]

mypy/typeanal.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __init__(self,
7070
def visit_unbound_type(self, t: UnboundType) -> Type:
7171
sym = self.lookup(t.name, t)
7272
if sym is not None:
73+
fullname = sym.node.fullname()
7374
if sym.kind == TVAR:
7475
if len(t.args) > 0:
7576
self.fail('Type variable "{}" used with arguments'.format(
@@ -81,18 +82,24 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
8182
values = cast(TypeVarExpr, sym.node).values
8283
return TypeVar(t.name, sym.tvar_id, values, self.builtin_type('builtins.object'),
8384
t.line, rep)
84-
elif sym.node.fullname() == 'builtins.None':
85+
elif fullname == 'builtins.None':
8586
return Void()
86-
elif sym.node.fullname() == 'typing.Any':
87+
elif fullname == 'typing.Any':
8788
return AnyType()
88-
elif sym.node.fullname() == 'typing.Tuple':
89+
elif fullname == 'typing.Tuple':
8990
return TupleType(self.anal_array(t.args),
9091
self.builtin_type('builtins.tuple'))
91-
elif sym.node.fullname() == 'typing.Union':
92+
elif fullname == 'typing.Union':
9293
items = self.anal_array(t.args)
9394
items = [item for item in items if not isinstance(item, Void)]
9495
return UnionType.make_union(items)
95-
elif sym.node.fullname() == 'typing.Callable':
96+
elif fullname == 'typing.Optional':
97+
if len(t.args) != 1:
98+
self.fail('Optional[...] must have exactly one type argument', t)
99+
items = self.anal_array(t.args)
100+
# Currently Optional[t] is just an alias for t.
101+
return items[0]
102+
elif fullname == 'typing.Callable':
96103
return self.analyze_function_type(t)
97104
elif sym.kind == TYPE_ALIAS:
98105
# TODO: Generic type aliases.
@@ -109,7 +116,6 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
109116
Instance(info, [], t.line),
110117
t.line, t.repr)
111118
else:
112-
113119
# Analyze arguments and construct Instance type. The
114120
# number of type arguments and their values are
115121
# checked only later, since we do not always know the

stubs/2.7/typing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(self, target_type): pass
2626
def __getitem__(self, typeargs): pass
2727

2828
Union = TypeAlias(object)
29+
Optional = TypeAlias(object)
2930
List = TypeAlias(object)
3031
Dict = TypeAlias(object)
3132
Set = TypeAlias(object)

stubs/3.2/typing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(self, target_type): pass
2626
def __getitem__(self, typeargs): pass
2727

2828
Union = TypeAlias(object)
29+
Optional = TypeAlias(object)
2930
List = TypeAlias(object)
3031
Dict = TypeAlias(object)
3132
Set = TypeAlias(object)

0 commit comments

Comments
 (0)