Skip to content

Commit a6d01e5

Browse files
committed
Merge remote-tracking branch 'origin/master' into fix1698
2 parents e53c355 + d4e15f9 commit a6d01e5

File tree

104 files changed

+1195
-1272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+1195
-1272
lines changed

docs/source/cheat_sheet.rst

+11-6
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ Built-in types
1919

2020
.. code-block:: python
2121
22-
from typing import List, Set, Dict, Tuple, Optional
23-
import six
22+
from typing import List, Set, Dict, Tuple, Text, Optional
2423
2524
# For simple built-in types, just use the name of the type.
2625
x = 1 # type: int
@@ -39,9 +38,9 @@ Built-in types
3938
# For tuples, we specify the types of all the elements.
4039
x = (3, "yes", 7.5) # type: Tuple[int, str, float]
4140
42-
# For textual data, we generally use six.text_type.
41+
# For textual data, use Text.
4342
# This is `unicode` in Python 2 and `str` in Python 3.
44-
x = ["string", u"unicode"] # type: List[six.text_type]
43+
x = ["string", u"unicode"] # type: List[Text]
4544
4645
# Use Optional for values that could be None.
4746
input_str = f() # type: Optional[str]
@@ -177,11 +176,17 @@ Other stuff
177176
.. code-block:: python
178177
179178
# typing.Match describes regex matches from the re module.
180-
from typing import Match
179+
from typing import Match, AnyStr
181180
x = re.match(r'[0-9]+', "15") # type: Match[str]
182181
182+
# Use AnyStr for functions that should accept any kind of string
183+
# without allowing different kinds of strings to mix.
184+
def concat(a: AnyStr, b: AnyStr) -> AnyStr:
185+
return a + b
186+
concat(u"foo", u"bar") # type: unicode
187+
concat(b"foo", b"bar") # type: bytes
188+
183189
# TODO: add typing.IO: e.g., sys.stdout has type IO[str]
184190
185191
# TODO: add TypeVar and a simple generic function
186192
187-
# TODO: add AnyStr (and mention up next to strings)

docs/source/duck_type_compatibility.rst

+3
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ and also behaves as expected:
3535
silently pass type checking. In Python 3 ``str`` and ``bytes`` are
3636
separate, unrelated types and this kind of error is easy to
3737
detect. This a good reason for preferring Python 3 over Python 2!
38+
39+
See :ref:`text-and-anystr` for details on how to enforce that a
40+
value must be a unicode string in a cross-compatible way.

docs/source/kinds_of_types.rst

+44
Original file line numberDiff line numberDiff line change
@@ -657,3 +657,47 @@ Now mypy will infer the correct type of the result when we call
657657

658658
For more details about ``Type[]`` see `PEP 484
659659
<https://www.python.org/dev/peps/pep-0484/#the-type-of-class-objects>`_.
660+
661+
.. _text-and-anystr:
662+
663+
Text and AnyStr
664+
***************
665+
666+
Sometimes you may want to write a function which will accept only unicode
667+
strings. This can be challenging to do in a codebase intended to run in
668+
both Python 2 and Python 3 since ``str`` means something different in both
669+
versions and ``unicode`` is not a keyword in Python 3.
670+
671+
To help solve this issue, use ``typing.Text`` which is aliased to
672+
``unicode`` in Python 2 and to ``str`` in Python 3. This allows you to
673+
indicate that a function should accept only unicode strings in a
674+
cross-compatible way:
675+
676+
.. code-block:: python
677+
678+
from typing import Text
679+
680+
def unicode_only(s: Text) -> Text:
681+
return s + u'\u2713'
682+
683+
In other cases, you may want to write a function that will work with any
684+
kind of string but will not let you mix two different string types. To do
685+
so use ``typing.AnyStr``:
686+
687+
.. code-block:: python
688+
689+
from typing import AnyStr
690+
691+
def concat(x: AnyStr, y: AnyStr) -> AnyStr:
692+
return x + y
693+
694+
concat('a', 'b') # Okay
695+
concat(b'a', b'b') # Okay
696+
concat('a', b'b') # Error: cannot mix bytes and unicode
697+
698+
For more details, see :ref:`type-variable-value-restriction`.
699+
700+
.. note::
701+
702+
How ``bytes``, ``str``, and ``unicode`` are handled between Python 2 and
703+
Python 3 may change in future versions of mypy.

mypy/build.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ def wrap_context(self) -> Iterator[None]:
11811181
except CompileError:
11821182
raise
11831183
except Exception as err:
1184-
report_internal_error(err, self.path, 0)
1184+
report_internal_error(err, self.path, 0, self.manager.errors)
11851185
self.manager.errors.set_import_context(save_import_context)
11861186
self.check_blockers()
11871187

@@ -1344,6 +1344,7 @@ def load_graph(sources: List[BuildSource], manager: BuildManager) -> Graph:
13441344
# TODO: Consider whether to go depth-first instead. This may
13451345
# affect the order in which we process files within import cycles.
13461346
new = collections.deque() # type: collections.deque[State]
1347+
entry_points = set() # type: Set[str]
13471348
# Seed the graph with the initial root sources.
13481349
for bs in sources:
13491350
try:
@@ -1356,11 +1357,16 @@ def load_graph(sources: List[BuildSource], manager: BuildManager) -> Graph:
13561357
manager.errors.raise_error()
13571358
graph[st.id] = st
13581359
new.append(st)
1360+
entry_points.add(bs.module)
13591361
# Collect dependencies. We go breadth-first.
13601362
while new:
13611363
st = new.popleft()
1362-
for dep in st.ancestors + st.dependencies:
1363-
if dep not in graph:
1364+
for dep in st.ancestors + st.dependencies + st.suppressed:
1365+
# We don't want to recheck imports marked with '# type: ignore'
1366+
# so we ignore any suppressed module not explicitly re-included
1367+
# from the command line.
1368+
ignored = dep in st.suppressed and dep not in entry_points
1369+
if dep not in graph and not ignored:
13641370
try:
13651371
if dep in st.ancestors:
13661372
# TODO: Why not 'if dep not in st.dependencies' ?
@@ -1380,6 +1386,11 @@ def load_graph(sources: List[BuildSource], manager: BuildManager) -> Graph:
13801386
new.append(newst)
13811387
if dep in st.ancestors and dep in graph:
13821388
graph[dep].child_modules.add(st.id)
1389+
if dep in graph and dep in st.suppressed:
1390+
# Previously suppressed file is now visible
1391+
if dep in st.suppressed:
1392+
st.suppressed.remove(dep)
1393+
st.dependencies.append(dep)
13831394
for id, g in graph.items():
13841395
if g.has_new_submodules():
13851396
g.parse_file()

mypy/checker.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def accept(self, node: Node, type_context: Type = None) -> Type:
201201
try:
202202
typ = node.accept(self)
203203
except Exception as err:
204-
report_internal_error(err, self.errors.file, node.line)
204+
report_internal_error(err, self.errors.file, node.line, self.errors)
205205
self.type_context.pop()
206206
self.store_type(node, typ)
207207
if self.typing_mode_none():
@@ -809,9 +809,13 @@ def check_method_override_for_base_with_name(
809809
# Map the overridden method type to subtype context so that
810810
# it can be checked for compatibility.
811811
original_type = base_attr.type
812-
if original_type is None and isinstance(base_attr.node,
813-
FuncDef):
814-
original_type = self.function_type(base_attr.node)
812+
if original_type is None:
813+
if isinstance(base_attr.node, FuncDef):
814+
original_type = self.function_type(base_attr.node)
815+
elif isinstance(base_attr.node, Decorator):
816+
original_type = self.function_type(base_attr.node.func)
817+
else:
818+
assert False, str(base_attr.node)
815819
if isinstance(original_type, FunctionLike):
816820
original = map_type_from_supertype(
817821
method_type(original_type),
@@ -825,7 +829,6 @@ def check_method_override_for_base_with_name(
825829
base.name(),
826830
defn)
827831
else:
828-
assert original_type is not None
829832
self.msg.signature_incompatible_with_supertype(
830833
defn.name(), name, base.name(), defn)
831834

@@ -2248,7 +2251,8 @@ def leave_partial_types(self) -> None:
22482251
partial_types = self.partial_types.pop()
22492252
if not self.current_node_deferred:
22502253
for var, context in partial_types.items():
2251-
if experiments.STRICT_OPTIONAL and cast(PartialType, var.type).type is None:
2254+
if (experiments.STRICT_OPTIONAL and
2255+
isinstance(var.type, PartialType) and var.type.type is None):
22522256
# None partial type: assume variable is intended to have type None
22532257
var.type = NoneTyp()
22542258
else:

mypy/checkexpr.py

+57-42
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,10 @@ def try_infer_partial_type(self, e: CallExpr) -> None:
158158
var = cast(Var, e.callee.expr.node)
159159
partial_types = self.chk.find_partial_types(var)
160160
if partial_types is not None and not self.chk.current_node_deferred:
161-
partial_type = cast(PartialType, var.type)
162-
if partial_type is None or partial_type.type is None:
161+
partial_type = var.type
162+
if (partial_type is None or
163+
not isinstance(partial_type, PartialType) or
164+
partial_type.type is None):
163165
# A partial None type -> can't infer anything.
164166
return
165167
typename = partial_type.type.fullname()
@@ -1538,55 +1540,68 @@ def check_generator_or_comprehension(self, gen: GeneratorExpr,
15381540
type_name: str,
15391541
id_for_messages: str) -> Type:
15401542
"""Type check a generator expression or a list comprehension."""
1541-
self.check_for_comp(gen)
1543+
with self.chk.binder.frame_context():
1544+
self.check_for_comp(gen)
15421545

1543-
# Infer the type of the list comprehension by using a synthetic generic
1544-
# callable type.
1545-
tvdef = TypeVarDef('T', -1, [], self.chk.object_type())
1546-
tv = TypeVarType(tvdef)
1547-
constructor = CallableType(
1548-
[tv],
1549-
[nodes.ARG_POS],
1550-
[None],
1551-
self.chk.named_generic_type(type_name, [tv]),
1552-
self.chk.named_type('builtins.function'),
1553-
name=id_for_messages,
1554-
variables=[tvdef])
1555-
return self.check_call(constructor,
1556-
[gen.left_expr], [nodes.ARG_POS], gen)[0]
1546+
# Infer the type of the list comprehension by using a synthetic generic
1547+
# callable type.
1548+
tvdef = TypeVarDef('T', -1, [], self.chk.object_type())
1549+
tv = TypeVarType(tvdef)
1550+
constructor = CallableType(
1551+
[tv],
1552+
[nodes.ARG_POS],
1553+
[None],
1554+
self.chk.named_generic_type(type_name, [tv]),
1555+
self.chk.named_type('builtins.function'),
1556+
name=id_for_messages,
1557+
variables=[tvdef])
1558+
return self.check_call(constructor,
1559+
[gen.left_expr], [nodes.ARG_POS], gen)[0]
15571560

15581561
def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type:
15591562
"""Type check a dictionary comprehension."""
1560-
self.check_for_comp(e)
1561-
1562-
# Infer the type of the list comprehension by using a synthetic generic
1563-
# callable type.
1564-
ktdef = TypeVarDef('KT', -1, [], self.chk.object_type())
1565-
vtdef = TypeVarDef('VT', -2, [], self.chk.object_type())
1566-
kt = TypeVarType(ktdef)
1567-
vt = TypeVarType(vtdef)
1568-
constructor = CallableType(
1569-
[kt, vt],
1570-
[nodes.ARG_POS, nodes.ARG_POS],
1571-
[None, None],
1572-
self.chk.named_generic_type('builtins.dict', [kt, vt]),
1573-
self.chk.named_type('builtins.function'),
1574-
name='<dictionary-comprehension>',
1575-
variables=[ktdef, vtdef])
1576-
return self.check_call(constructor,
1577-
[e.key, e.value], [nodes.ARG_POS, nodes.ARG_POS], e)[0]
1563+
with self.chk.binder.frame_context():
1564+
self.check_for_comp(e)
1565+
1566+
# Infer the type of the list comprehension by using a synthetic generic
1567+
# callable type.
1568+
ktdef = TypeVarDef('KT', -1, [], self.chk.object_type())
1569+
vtdef = TypeVarDef('VT', -2, [], self.chk.object_type())
1570+
kt = TypeVarType(ktdef)
1571+
vt = TypeVarType(vtdef)
1572+
constructor = CallableType(
1573+
[kt, vt],
1574+
[nodes.ARG_POS, nodes.ARG_POS],
1575+
[None, None],
1576+
self.chk.named_generic_type('builtins.dict', [kt, vt]),
1577+
self.chk.named_type('builtins.function'),
1578+
name='<dictionary-comprehension>',
1579+
variables=[ktdef, vtdef])
1580+
return self.check_call(constructor,
1581+
[e.key, e.value], [nodes.ARG_POS, nodes.ARG_POS], e)[0]
15781582

15791583
def check_for_comp(self, e: Union[GeneratorExpr, DictionaryComprehension]) -> None:
15801584
"""Check the for_comp part of comprehensions. That is the part from 'for':
15811585
... for x in y if z
1586+
1587+
Note: This adds the type information derived from the condlists to the current binder.
15821588
"""
1583-
with self.chk.binder.frame_context():
1584-
for index, sequence, conditions in zip(e.indices, e.sequences,
1585-
e.condlists):
1586-
sequence_type = self.chk.analyze_iterable_item_type(sequence)
1587-
self.chk.analyze_index_variables(index, sequence_type, e)
1588-
for condition in conditions:
1589-
self.accept(condition)
1589+
for index, sequence, conditions in zip(e.indices, e.sequences,
1590+
e.condlists):
1591+
sequence_type = self.chk.analyze_iterable_item_type(sequence)
1592+
self.chk.analyze_index_variables(index, sequence_type, e)
1593+
for condition in conditions:
1594+
self.accept(condition)
1595+
1596+
# values are only part of the comprehension when all conditions are true
1597+
true_map, _ = mypy.checker.find_isinstance_check(
1598+
condition, self.chk.type_map,
1599+
self.chk.typing_mode_weak()
1600+
)
1601+
1602+
if true_map:
1603+
for var, type in true_map.items():
1604+
self.chk.binder.push(var, type)
15901605

15911606
def visit_conditional_expr(self, e: ConditionalExpr) -> Type:
15921607
cond_type = self.accept(e.cond)

0 commit comments

Comments
 (0)