diff --git a/CREDITS b/CREDITS index 6ae070531a16..ebfc62ae15f0 100644 --- a/CREDITS +++ b/CREDITS @@ -9,6 +9,7 @@ Contributors (in alphabetical order): Steven Allen (@Stebalien) Reid Barton (@rwbarton) + Miguel Garcia (@rockneurotiko) Ryan Gonzalez (@kirbyfan64) Ashley Hewson (@ashleyh) Bob Ippolito (@etrepum) @@ -17,7 +18,6 @@ Contributors (in alphabetical order): Florian Ludwig (@FlorianLudwig) Jared Pochtar (@jaredp) Eric Price (@ecprice) - rockneurotiko (@rockneurotiko) Ron Murawski Sebastian Riikonen Schuyler Smith diff --git a/docs/make.bat b/docs/make.bat old mode 100644 new mode 100755 diff --git a/misc/remove-eol-whitespace.sh b/misc/remove-eol-whitespace.sh old mode 100755 new mode 100644 diff --git a/mypy/__init__.py~HEAD b/mypy/__init__.py~HEAD deleted file mode 100644 index fa4000266849..000000000000 --- a/mypy/__init__.py~HEAD +++ /dev/null @@ -1 +0,0 @@ -# This page intentionally left blank diff --git a/mypy/checker.py b/mypy/checker.py index 2cecb7a9189b..1d43fe35b637 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -16,7 +16,7 @@ TypeApplication, DictExpr, SliceExpr, FuncExpr, TempNode, SymbolTableNode, Context, ListComprehension, ConditionalExpr, GeneratorExpr, Decorator, SetExpr, PassStmt, TypeVarExpr, UndefinedExpr, PrintStmt, - LITERAL_TYPE, BreakStmt, ContinueStmt, ComparisonExpr + LITERAL_TYPE, BreakStmt, ContinueStmt, ComparisonExpr, YieldFromExpr, YieldFromStmt ) from mypy.nodes import function_type, method_type from mypy import nodes @@ -1193,18 +1193,42 @@ def visit_return_stmt(self, s: ReturnStmt) -> Type: not isinstance(typ, NoneTyp)): self.fail(messages.NO_RETURN_VALUE_EXPECTED, s) else: + if self.function_stack[-1].is_coroutine: # Something similar will be needed to mix return and yield + # If the function is a coroutine, wrap the return type in a Future + typ = self.wrap_generic_type(cast(Instance,typ), cast(Instance,self.return_types[-1]), 'asyncio.futures.Future', s) self.check_subtype( typ, self.return_types[-1], s, messages.INCOMPATIBLE_RETURN_VALUE_TYPE + ": expected {}, got {}".format(self.return_types[-1], typ) ) else: - # Return without a value. It's valid in a generator function. - if not self.function_stack[-1].is_generator: + # Return without a value. It's valid in a generator and coroutine function. + if not self.function_stack[-1].is_generator and not self.function_stack[-1].is_coroutine: if (not isinstance(self.return_types[-1], Void) and not self.is_dynamic_function()): self.fail(messages.RETURN_VALUE_EXPECTED, s) + def wrap_generic_type(self, typ: Instance, rtyp: Instance, check_type: str, context: Context) -> Type: + n_diff = self.count_nested_types(rtyp, check_type) - self.count_nested_types(typ, check_type) + if n_diff == 1: + return self.named_generic_type(check_type, [typ]) + elif n_diff == 0 or n_diff > 1: + self.fail(messages.INCOMPATIBLE_RETURN_VALUE_TYPE + + ": expected {}, got {}".format(rtyp, typ), context) + return typ + return typ + + def count_nested_types(self, typ: Instance, check_type: str) -> int: + c = 0 + while is_subtype(typ, self.named_type(check_type)): + c += 1 + typ = map_instance_to_supertype(self.named_generic_type(check_type, typ.args), self.lookup_typeinfo(check_type)) + if typ.args: + typ = cast(Instance,typ.args[0]) + else: + return c + return c + def visit_yield_stmt(self, s: YieldStmt) -> Type: return_type = self.return_types[-1] if isinstance(return_type, Instance): @@ -1225,6 +1249,56 @@ def visit_yield_stmt(self, s: YieldStmt) -> Type: messages.INCOMPATIBLE_TYPES_IN_YIELD, 'actual type', 'expected type') + def visit_yield_from_stmt(self, s: YieldFromStmt) -> Type: + return_type = self.return_types[-1] + type_func = self.accept(s.expr, return_type) + if isinstance(type_func, Instance): + if type_func.type.fullname() == 'asyncio.futures.Future': + # if is a Future, in stmt don't need to do nothing + # because the type Future[Some] jus matters to the main loop + # that python executes, in statement we shouldn't get the Future, + # is just for async purposes. + self.function_stack[-1].is_coroutine = True # Set the function as coroutine + elif is_subtype(type_func, self.named_type('typing.Iterable')): + # If it's and Iterable-Like, let's check the types. + # Maybe just check if have __iter__? (like in analyse_iterable) + self.check_iterable_yield_from(s) + else: + self.msg.yield_from_invalid_operand_type(type_func, s) + elif isinstance(type_func, AnyType): + self.check_iterable_yield_from(s) + else: + self.msg.yield_from_invalid_operand_type(type_func, s) + + def check_iterable_yield_from(self, s: YieldFromStmt) -> Type: + """ + Check that return type is super type of Iterable (Maybe just check if have __iter__?) + and compare it with the type of the expression + """ + expected_item_type = self.return_types[-1] + if isinstance(expected_item_type, Instance): + if not is_subtype(expected_item_type, self.named_type('typing.Iterable')): + self.fail(messages.INVALID_RETURN_TYPE_FOR_YIELD_FROM, s) + return None + elif expected_item_type.args: + expected_item_type = map_instance_to_supertype(expected_item_type, self.lookup_typeinfo('typing.Iterable')) + expected_item_type = expected_item_type.args[0] # Take the item inside the iterator + elif isinstance(expected_item_type, AnyType): + expected_item_type = AnyType() + else: + self.fail(messages.INVALID_RETURN_TYPE_FOR_YIELD_FROM, s) + return None + if s.expr is None: + actual_item_type = Void() # type: Type + else: + actual_item_type = self.accept(s.expr, expected_item_type) + if hasattr(actual_item_type, 'args') and cast(Instance,actual_item_type).args: + actual_item_type = map_instance_to_supertype(cast(Instance,actual_item_type), self.lookup_typeinfo('typing.Iterable')) + actual_item_type = actual_item_type.args[0] # Take the item inside the iterator + self.check_subtype(actual_item_type, expected_item_type, s, + messages.INCOMPATIBLE_TYPES_IN_YIELD_FROM, + 'actual type', 'expected type') + def visit_if_stmt(self, s: IfStmt) -> Type: """Type check an if statement.""" broken = True @@ -1527,6 +1601,23 @@ def visit_call_expr(self, e: CallExpr) -> Type: self.breaking_out = False return result + def visit_yield_from_expr(self, e: YieldFromExpr) -> Type: + # result = self.expr_checker.visit_yield_from_expr(e) + result = self.accept(e.expr) + result_instance = cast(Instance, result) + if result_instance.type.fullname() == "asyncio.futures.Future": + self.function_stack[-1].is_coroutine = True # Set the function as coroutine + result = result_instance.args[0] # Set the return type as the type inside + elif is_subtype(result, self.named_type('typing.Iterable')): + # TODO + # Check return type Iterator[Some] + # Maybe set result like in the Future + pass + else: + # self.msg.yield_from_invalid_operand_type(e.expr, e) + self.msg.yield_from_invalid_operand_type(e.expr.accept(self), e) + return result + def visit_member_expr(self, e: MemberExpr) -> Type: return self.expr_checker.visit_member_expr(e) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index f6e5e68f5e8e..3459bef31665 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -159,7 +159,7 @@ def check_mapping_str_interpolation(self, specifiers: List[ConversionSpecifier], 'expression has type', 'expected type for mapping is') def build_replacement_checkers(self, specifiers: List[ConversionSpecifier], - context: Context) -> List[ Tuple[ Function[[Node], None], + context: Context) -> List[ Tuple[ Function[[Node], None], Function[[Type], None] ] ]: checkers = [] # type: List[ Tuple[ Function[[Node], None], Function[[Type], None] ] ] for specifier in specifiers: @@ -170,10 +170,10 @@ def build_replacement_checkers(self, specifiers: List[ConversionSpecifier], return checkers def replacement_checkers(self, specifier: ConversionSpecifier, - context: Context) -> List[ Tuple[ Function[[Node], None], + context: Context) -> List[ Tuple[ Function[[Node], None], Function[[Type], None] ] ]: """Returns a list of tuples of two functions that check whether a replacement is - of the right type for the specifier. The first functions take a node and checks + of the right type for the specifier. The first functions take a node and checks its type in the right type context. The second function just checks a type. """ checkers = [] # type: List[ Tuple[ Function[[Node], None], Function[[Type], None] ] ] @@ -194,7 +194,7 @@ def replacement_checkers(self, specifier: ConversionSpecifier, checkers.append(c) return checkers - def checkers_for_star(self, context: Context) -> Tuple[ Function[[Node], None], + def checkers_for_star(self, context: Context) -> Tuple[ Function[[Node], None], Function[[Type], None] ]: """Returns a tuple of check functions that check whether, respectively, a node or a type is compatible with a star in a conversion specifier @@ -211,10 +211,10 @@ def check_node(node: Node) -> None: return check_node, check_type - def checkers_for_regular_type(self, type: str, context: Context) -> Tuple[ Function[[Node], None], + def checkers_for_regular_type(self, type: str, context: Context) -> Tuple[ Function[[Node], None], Function[[Type], None] ]: """Returns a tuple of check functions that check whether, respectively, - a node or a type is compatible with 'type'. Return None in case of an + a node or a type is compatible with 'type'. Return None in case of an """ expected_type = self.conversion_type(type, context) if expected_type == None: @@ -231,7 +231,7 @@ def check_node(node: Node) -> None: return check_node, check_type - def checkers_for_c_type(self, type: str, context: Context) -> Tuple[ Function[[Node], None], + def checkers_for_c_type(self, type: str, context: Context) -> Tuple[ Function[[Node], None], Function[[Type], None] ]: """Returns a tuple of check functions that check whether, respectively, a node or a type is compatible with 'type' that is a character type diff --git a/mypy/messages.py b/mypy/messages.py index a6fd4bc68948..c3fb9ebdb80d 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -32,9 +32,12 @@ INVALID_EXCEPTION_TYPE = 'Exception type must be derived from BaseException' INVALID_RETURN_TYPE_FOR_YIELD = \ 'Iterator function return type expected for "yield"' +INVALID_RETURN_TYPE_FOR_YIELD_FROM = \ + 'Iterable function return type expected for "yield from"' INCOMPATIBLE_TYPES = 'Incompatible types' INCOMPATIBLE_TYPES_IN_ASSIGNMENT = 'Incompatible types in assignment' INCOMPATIBLE_TYPES_IN_YIELD = 'Incompatible types in yield' +INCOMPATIBLE_TYPES_IN_YIELD_FROM = 'Incompatible types in "yield from"' INCOMPATIBLE_TYPES_IN_STR_INTERPOLATION = 'Incompatible types in string interpolation' INIT_MUST_NOT_HAVE_RETURN_TYPE = 'Cannot define return type for "__init__"' GETTER_TYPE_INCOMPATIBLE_WITH_SETTER = \ @@ -671,6 +674,11 @@ def signatures_incompatible(self, method: str, other_method: str, self.fail('Signatures of "{}" and "{}" are incompatible'.format( method, other_method), context) + def yield_from_invalid_operand_type(self, expr: Type, context: Context) -> Type: + text = self.format(expr) if self.format(expr) != 'object' else expr + self.fail('"yield from" can\'t be applied to {}'.format(text), context) + return AnyType() + def capitalize(s: str) -> str: """Capitalize the first character of a string.""" @@ -721,4 +729,4 @@ def callable_name(type: Callable) -> str: def temp_message_builder() -> MessageBuilder: """Return a message builder usable for collecting errors locally.""" - return MessageBuilder(Errors()) + return MessageBuilder(Errors()) \ No newline at end of file diff --git a/mypy/nodes.py b/mypy/nodes.py index b6380fbd52e4..c165290720b2 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -238,6 +238,7 @@ class FuncItem(FuncBase): is_overload = False # Is this an overload variant of function with # more than one overload variant? is_generator = False # Contains a yield statement? + is_coroutine = False # Contains @coroutine or yield from Future is_static = False # Uses @staticmethod? is_class = False # Uses @classmethod? expanded = Undefined(List['FuncItem']) # Variants of function with type @@ -615,6 +616,16 @@ def accept(self, visitor: NodeVisitor[T]) -> T: return visitor.visit_yield_stmt(self) +class YieldFromStmt(Node): + expr = Undefined(Node) + + def __init__(self, expr: Node) -> None: + self.expr = expr + + def accept(self, visitor: NodeVisitor[T]) -> T: + return visitor.visit_yield_from_stmt(self) + + class DelStmt(Node): expr = Undefined(Node) @@ -906,6 +917,16 @@ def accept(self, visitor: NodeVisitor[T]) -> T: return visitor.visit_call_expr(self) +class YieldFromExpr(Node): + expr = Undefined(Node) + + def __init__(self, expr: Node) -> None: + self.expr = expr + + def accept(self, visitor: NodeVisitor[T]) -> T: + return visitor.visit_yield_from_expr(self) + + class IndexExpr(Node): """Index expression x[y]. diff --git a/mypy/output.py b/mypy/output.py index 3668c92804c3..a08daa067d0b 100644 --- a/mypy/output.py +++ b/mypy/output.py @@ -226,6 +226,9 @@ def visit_assert_stmt(self, o): def visit_yield_stmt(self, o): self.simple_stmt(o, o.expr) + def visit_yield_from_stmt(self, o): + self.simple_stmt(o, o.expr) + def visit_del_stmt(self, o): self.simple_stmt(o, o.expr) @@ -355,6 +358,9 @@ def visit_slice_expr(self, o): self.token(o.repr.colon2) self.node(o.stride) + def visit_yield_from_expr(self, o): + o.expr.accept(self) + def visit_call_expr(self, o): r = o.repr self.node(o.callee) diff --git a/mypy/parse.py b/mypy/parse.py old mode 100755 new mode 100644 index 443b642554dd..5c04ee1b3b08 --- a/mypy/parse.py +++ b/mypy/parse.py @@ -6,7 +6,7 @@ import re -from typing import Undefined, List, Tuple, Any, Set, cast +from typing import Undefined, List, Tuple, Any, Set, cast, Union from mypy import lex from mypy.lex import ( @@ -23,7 +23,8 @@ TupleExpr, GeneratorExpr, ListComprehension, ListExpr, ConditionalExpr, DictExpr, SetExpr, NameExpr, IntExpr, StrExpr, BytesExpr, UnicodeExpr, FloatExpr, CallExpr, SuperExpr, MemberExpr, IndexExpr, SliceExpr, OpExpr, - UnaryExpr, FuncExpr, TypeApplication, PrintStmt, ImportBase, ComparisonExpr + UnaryExpr, FuncExpr, TypeApplication, PrintStmt, ImportBase, ComparisonExpr, YieldFromStmt, + YieldFromExpr ) from mypy import nodes from mypy import noderepr @@ -743,6 +744,8 @@ def parse_return_stmt(self) -> ReturnStmt: expr = None # type: Node if not isinstance(self.current(), Break): expr = self.parse_expression() + if isinstance(expr, YieldFromExpr): # "yield from" expressions can't be returned. + return None br = self.expect_break() node = ReturnStmt(expr) self.set_repr(node, noderepr.SimpleStmtRepr(return_tok, br)) @@ -771,16 +774,41 @@ def parse_assert_stmt(self) -> AssertStmt: self.set_repr(node, noderepr.SimpleStmtRepr(assert_tok, br)) return node - def parse_yield_stmt(self) -> YieldStmt: + def parse_yield_stmt(self) -> Union[YieldStmt, YieldFromStmt]: yield_tok = self.expect('yield') expr = None # type: Node + node = YieldStmt(expr) if not isinstance(self.current(), Break): - expr = self.parse_expression() + if isinstance(self.current(), Keyword) and self.current_str() == "from": # Not go if it's not from + from_tok = self.expect("from") + expr = self.parse_expression() # Here comes when yield from is not assigned + node_from = YieldFromStmt(expr) + br = self.expect_break() + self.set_repr(node_from, noderepr.SimpleStmtRepr(yield_tok, br)) + return node_from # return here, we've gotted the type + else: + expr = self.parse_expression() + node = YieldStmt(expr) br = self.expect_break() - node = YieldStmt(expr) self.set_repr(node, noderepr.SimpleStmtRepr(yield_tok, br)) return node + def parse_yield_from_expr(self) -> YieldFromExpr: + y_tok = self.expect("yield") + expr = None # type: Node + node = YieldFromExpr(expr) + if self.current_str() == "from": + f_tok = self.expect("from") + tok = self.parse_expression() # Here comes when yield from is assigned to a variable + node = YieldFromExpr(tok) + else: + # TODO + # Here comes the yield expression (ex: x = yield 3 ) + # tok = self.parse_expression() + # node = YieldExpr(tok) # Doesn't exist now + pass + return node + def parse_del_stmt(self) -> DelStmt: del_tok = self.expect('del') expr = self.parse_expression() @@ -1054,6 +1082,8 @@ def parse_expression(self, prec: int = 0) -> Node: expr = self.parse_unicode_literal() elif isinstance(self.current(), FloatLit): expr = self.parse_float_expr() + elif isinstance(t, Keyword) and s == "yield": + expr = self.parse_yield_from_expr() # The expression yield from and yield to assign else: # Invalid expression. self.parse_error() diff --git a/mypy/semanal.py b/mypy/semanal.py index e8d810308eaa..b064ad40c048 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -56,7 +56,7 @@ SymbolTableNode, TVAR, UNBOUND_TVAR, ListComprehension, GeneratorExpr, FuncExpr, MDEF, FuncBase, Decorator, SetExpr, UndefinedExpr, TypeVarExpr, StrExpr, PrintStmt, ConditionalExpr, DucktypeExpr, DisjointclassExpr, - ComparisonExpr, ARG_POS, ARG_NAMED, MroError, type_aliases + ComparisonExpr, ARG_POS, ARG_NAMED, MroError, type_aliases, YieldFromStmt, YieldFromExpr ) from mypy.visitor import NodeVisitor from mypy.traverser import TraverserVisitor @@ -952,6 +952,9 @@ def visit_decorator(self, dec: Decorator) -> None: removed.append(i) dec.func.is_abstract = True self.check_decorated_function_is_method('abstractmethod', dec) + elif refers_to_fullname(d, 'asyncio.tasks.coroutine'): + removed.append(i) + dec.func.is_coroutine = True elif refers_to_fullname(d, 'builtins.staticmethod'): removed.append(i) dec.func.is_static = True @@ -1007,6 +1010,12 @@ def visit_yield_stmt(self, s: YieldStmt) -> None: if s.expr: s.expr.accept(self) + def visit_yield_from_stmt(self, s: YieldFromStmt) -> None: + if not self.is_func_scope(): + self.fail("'yield from' outside function", s) + if s.expr: + s.expr.accept(self) + def visit_assert_stmt(self, s: AssertStmt) -> None: if s.expr: s.expr.accept(self) @@ -1130,6 +1139,12 @@ def visit_dict_expr(self, expr: DictExpr) -> None: def visit_paren_expr(self, expr: ParenExpr) -> None: expr.expr.accept(self) + def visit_yield_from_expr(self, e: YieldFromExpr) -> None: + if not self.is_func_scope(): # not sure + self.fail("'yield from' outside function", e) + if e.expr: + e.expr.accept(self) + def visit_call_expr(self, expr: CallExpr) -> None: """Analyze a call expression. diff --git a/mypy/stats.py b/mypy/stats.py index c5e00411c853..9fcbd2ae9a21 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -14,7 +14,7 @@ from mypy import nodes from mypy.nodes import ( Node, FuncDef, TypeApplication, AssignmentStmt, NameExpr, CallExpr, - MemberExpr, OpExpr, ComparisonExpr, IndexExpr, UnaryExpr + MemberExpr, OpExpr, ComparisonExpr, IndexExpr, UnaryExpr, YieldFromExpr ) @@ -109,6 +109,10 @@ def visit_name_expr(self, o: NameExpr) -> None: self.process_node(o) super().visit_name_expr(o) + def visit_yield_from_expr(self, o: YieldFromExpr) -> None: + if o.expr: + o.expr.accept(self) + def visit_call_expr(self, o: CallExpr) -> None: self.process_node(o) if o.analyzed: diff --git a/mypy/strconv.py b/mypy/strconv.py index c0ba5c17979f..6ec31999350f 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -234,6 +234,9 @@ def visit_assert_stmt(self, o): def visit_yield_stmt(self, o): return self.dump([o.expr], o) + def visit_yield_from_stmt(self, o): + return self.dump([o.expr], o) + def visit_del_stmt(self, o): return self.dump([o.expr], o) @@ -319,6 +322,12 @@ def visit_member_expr(self, o): return self.dump([o.expr, self.pretty_name(o.name, o.kind, o.fullname, o.is_def)], o) + def visit_yield_from_expr(self, o): + if o.expr: + return self.dump([o.expr.accept(self)], o) + else: + return self.dump([], o) + def visit_call_expr(self, o): if o.analyzed: return o.analyzed.accept(self) diff --git a/mypy/test/data/check-statements.test b/mypy/test/data/check-statements.test index f99786c4057b..d309dca711d3 100644 --- a/mypy/test/data/check-statements.test +++ b/mypy/test/data/check-statements.test @@ -644,6 +644,88 @@ def f() -> Iterator[None]: [builtins fixtures/for.py] +-- Yield from statement +-- -------------------- + +-- Iterables +-- ---------- + +[case testSimpleYieldFromWithIterator] +from typing import Iterator +def g() -> Iterator[str]: + yield '42' +def h() -> Iterator[int]: + yield 42 +def f() -> Iterator[str]: + yield from g() + yield from h() # E: Incompatible types in "yield from" (actual type "int", expected type "str") +[out] +main: In function "f": + +[case testYieldFromAppliedToAny] +from typing import Any +def g() -> Any: + yield object() +def f() -> Any: + yield from g() +[out] + +[case testYieldFromInFunctionReturningFunction] +from typing import Iterator, Function +def g() -> Iterator[int]: + yield 42 +def f() -> Function[[], None]: + yield from g() # E: Iterable function return type expected for "yield from" +[out] +main: In function "f": + +[case testYieldFromNotIterableReturnType] +from typing import Iterator +def g() -> Iterator[int]: + yield 42 +def f() -> int: + yield from g() # E: Iterable function return type expected for "yield from" +[out] +main: In function "f": + +[case testYieldFromNotAppliedIterator] +from typing import Iterator +def g() -> int: + return 42 +def f() -> Iterator[int]: + yield from g() # E: "yield from" can't be applied to "int" +[out] +main: In function "f": + +[case testYieldFromCheckIncompatibleTypesTwoIterables] +from typing import List, Iterator +def g() -> Iterator[List[int]]: + yield [2, 3, 4] +def f() -> Iterator[List[int]]: + yield from g() + yield from [1, 2, 3] # E: Incompatible types in "yield from" (actual type "int", expected type List[int]) +[builtins fixtures/for.py] +[out] +main: In function "f": + +[case testYieldFromNotAppliedToNothing] +def h(): + yield from # E: Parse error before end of line +[out] +main: In function "h": + +[case testYieldFromAndYieldTogether] +from typing import Iterator +def f() -> Iterator[str]: + yield "g1 ham" + yield from g() + yield "g1 eggs" +def g() -> Iterator[str]: + yield "g2 spam" + yield "g2 more spam" +[out] + + -- With statement -- -------------- diff --git a/mypy/test/data/parse-errors.test b/mypy/test/data/parse-errors.test index 6ce444272a8f..ee26e870b2a7 100644 --- a/mypy/test/data/parse-errors.test +++ b/mypy/test/data/parse-errors.test @@ -333,3 +333,17 @@ file, line 1: Parse error before "for" 1 if x else for y in z [out] file, line 1: Parse error before "for" + +[case testYieldFromNotRightParameter] +def f(): + yield from +[out] +file: In function "f": +file, line 2: Parse error before end of line + +[case testYieldFromAfterReturn] +def f(): + return yield from h() +[out] +file: In function "f": +file, line 2: Parse error before end of line diff --git a/mypy/test/data/parse.test b/mypy/test/data/parse.test index f56a2084115c..f079bc1a8341 100644 --- a/mypy/test/data/parse.test +++ b/mypy/test/data/parse.test @@ -614,7 +614,7 @@ MypyFile:1( NameExpr(i))) IntExpr(1) Tuple[a?[c?], Any?, int?])) - + [case testMultipleVarDef3] (xx, (z, i)) = 1 # type: (a[c], (Any, int)) [out] @@ -834,7 +834,7 @@ for x in y: for x, (y, w) in z: 1 for [x, (y, w)] in z: - 1 + 1 [out] MypyFile:1( ForStmt:1( @@ -1337,6 +1337,34 @@ MypyFile:1( NameExpr(x) IntExpr(1)))))) +[case testYieldFrom] +def f(): + yield from h() +[out] +MypyFile:1( + FuncDef:1( + f + Block:1( + YieldFromStmt:2( + CallExpr:2( + NameExpr(h) + Args()))))) + +[case testYieldFromAssignment] +def f(): + a = yield from h() +[out] +MypyFile:1( + FuncDef:1( + f + Block:1( + AssignmentStmt:2( + NameExpr(a) + YieldFromExpr:2( + CallExpr:2( + NameExpr(h) + Args())))))) + [case testDel] del x del x[0], y[1] diff --git a/mypy/test/data/pythoneval-asyncio.test b/mypy/test/data/pythoneval-asyncio.test new file mode 100644 index 000000000000..4695b539db26 --- /dev/null +++ b/mypy/test/data/pythoneval-asyncio.test @@ -0,0 +1,475 @@ +-- Test cases for type checking mypy programs using full stubs and running +-- using CPython. +-- +-- These are mostly regression tests -- no attempt is made to make these +-- complete. +-- +-- This test file check Asyncio and yield from interaction + +[case testImportAsyncio] +import asyncio +print('Imported') +[out] +Imported + +[case testSimpleCoroutineSleep] +from typing import Any +import asyncio +from asyncio import Future + +@asyncio.coroutine +def greet_every_two_seconds() -> 'Future[None]': + n = 0 + while n < 5: + print('Prev', n) + yield from asyncio.sleep(0.1) + print('After', n) + n += 1 + +loop = asyncio.get_event_loop() +try: + loop.run_until_complete(greet_every_two_seconds()) +finally: + loop.close() +[out] +Prev 0 +After 0 +Prev 1 +After 1 +Prev 2 +After 2 +Prev 3 +After 3 +Prev 4 +After 4 + +[case testCoroutineCallingOtherCoroutine] +import asyncio +from asyncio import Future + +@asyncio.coroutine +def compute(x: int, y: int) -> 'Future[int]': + print("Compute %s + %s ..." % (x, y)) + yield from asyncio.sleep(0.1) + return x + y # Here the int is wrapped in Future[int] + +@asyncio.coroutine +def print_sum(x: int, y: int) -> 'Future[None]': + result = yield from compute(x, y) # The type of result will be int (is extracted from Future[int] + print("%s + %s = %s" % (x, y, result)) + +loop = asyncio.get_event_loop() +loop.run_until_complete(print_sum(1, 2)) +loop.close() +[out] +Compute 1 + 2 ... +1 + 2 = 3 + +[case testCoroutineChangingFuture] +import asyncio +from asyncio import Future + +@asyncio.coroutine +def slow_operation(future: 'Future[str]') -> 'Future[None]': + yield from asyncio.sleep(0.1) + future.set_result('Future is done!') + +loop = asyncio.get_event_loop() +future = asyncio.Future() # type: Future[str] +asyncio.Task(slow_operation(future)) +loop.run_until_complete(future) +print(future.result()) +loop.close() +[out] +Future is done! + +[case testFunctionAssignedAsCallback] +import typing +import asyncio +from asyncio import Future, AbstractEventLoop + +@asyncio.coroutine +def slow_operation(future: 'Future[str]') -> 'Future[None]': + yield from asyncio.sleep(1) + future.set_result('Callback works!') + +def got_result(future: 'Future[str]') -> None: + print(future.result()) + loop.stop() + +loop = asyncio.get_event_loop() # type: AbstractEventLoop +future = asyncio.Future() # type: Future[str] +asyncio.Task(slow_operation(future)) # Here create a task with the function. (The Task need a Future[T] as first argument) +future.add_done_callback(got_result) # and assignt the callback to the future +try: + loop.run_forever() +finally: + loop.close() +[out] +Callback works! + +[case testMultipleTasks] +import typing +import asyncio +from asyncio import Task, Future +@asyncio.coroutine +def factorial(name, number) -> 'Future[None]': + f = 1 + for i in range(2, number+1): + print("Task %s: Compute factorial(%s)..." % (name, i)) + yield from asyncio.sleep(0.1) + f *= i + print("Task %s: factorial(%s) = %s" % (name, number, f)) + +loop = asyncio.get_event_loop() +tasks = [ + asyncio.Task(factorial("A", 2)), + asyncio.Task(factorial("B", 3)), + asyncio.Task(factorial("C", 4))] +loop.run_until_complete(asyncio.wait(tasks)) +loop.close() +[out] +Task A: Compute factorial(2)... +Task B: Compute factorial(2)... +Task C: Compute factorial(2)... +Task A: factorial(2) = 2 +Task B: Compute factorial(3)... +Task C: Compute factorial(3)... +Task B: factorial(3) = 6 +Task C: Compute factorial(4)... +Task C: factorial(4) = 24 + + +[case testConcatenatedCoroutines] +import typing +import asyncio +from asyncio import Future + +@asyncio.coroutine +def h4() -> 'Future[int]': + x = yield from future + return x + +@asyncio.coroutine +def h3() -> 'Future[int]': + x = yield from h4() + print("h3: %s" % x) + return x + +@asyncio.coroutine +def h2() -> 'Future[int]': + x = yield from h3() + print("h2: %s" % x) + return x + +@asyncio.coroutine +def h() -> 'Future[None]': + x = yield from h2() + print("h: %s" % x) + +loop = asyncio.get_event_loop() +future = asyncio.Future() # type: Future[int] +future.set_result(42) +loop.run_until_complete(h()) +print("Outside %s" % future.result()) +loop.close() +[out] +h3: 42 +h2: 42 +h: 42 +Outside 42 + +[case testConcantenatedCoroutinesReturningFutures] +import typing +import asyncio +from asyncio import Future + +@asyncio.coroutine +def h4() -> 'Future[Future[int]]': + yield from asyncio.sleep(0.1) + f = asyncio.Future() #type: Future[int] + return f + +@asyncio.coroutine +def h3() -> 'Future[Future[Future[int]]]': + x = yield from h4() + x.set_result(42) + f = asyncio.Future() #type: Future[Future[int]] + f.set_result(x) + return f + +@asyncio.coroutine +def h() -> 'Future[None]': + print("Before") + x = yield from h3() + y = yield from x + z = yield from y + print(z) + print(y) + print(x) + +loop = asyncio.get_event_loop() +loop.run_until_complete(h()) +loop.close() +[out] +Before +42 + +> + + +[case testCoroutineWithOwnClass] +import typing +import asyncio +from asyncio import Future + +class A: + def __init__(self, x: int) -> None: + self.x = x + +@asyncio.coroutine +def h() -> 'Future[None]': + x = yield from future + print("h: %s" % x.x) + +loop = asyncio.get_event_loop() +future = asyncio.Future() # type: Future[A] +future.set_result(A(42)) +loop.run_until_complete(h()) +print("Outside %s" % future.result().x) +loop.close() +[out] +h: 42 +Outside 42 + + +-- Errors + +[case testErrorAssigningCoroutineThatDontReturn] +from typing import Any +import asyncio +from asyncio import Future + +@asyncio.coroutine +def greet() -> 'Future[None]': + yield from asyncio.sleep(0.2) + print('Hello World') + +@asyncio.coroutine +def test() -> 'Future[None]': + yield from greet() + x = yield from greet() # Error + +loop = asyncio.get_event_loop() +try: + loop.run_until_complete(test()) +finally: + loop.close() +[out] +_program.py: In function "test": +_program.py, line 13: Function does not return a value + +[case testErrorReturnIsNotTheSameType] +import asyncio +from asyncio import Future + +@asyncio.coroutine +def compute(x: int, y: int) -> 'Future[int]': + print("Compute %s + %s ..." % (x, y)) + yield from asyncio.sleep(0.1) + return str(x + y) # Error + +@asyncio.coroutine +def print_sum(x: int, y: int) -> 'Future[None]': + result = yield from compute(x, y) + print("%s + %s = %s" % (x, y, result)) + +loop = asyncio.get_event_loop() +loop.run_until_complete(print_sum(1, 2)) +loop.close() + +[out] +_program.py: In function "compute": +_program.py, line 8: Incompatible return value type: expected asyncio.futures.Future[builtins.int], got asyncio.futures.Future[builtins.str] + +[case testErrorSetFutureDifferentInternalType] +import asyncio +from asyncio import Future + +@asyncio.coroutine +def slow_operation(future: 'Future[str]') -> 'Future[None]': + yield from asyncio.sleep(1) + future.set_result(42) # Error + +loop = asyncio.get_event_loop() +future = asyncio.Future() # type: Future[str] +asyncio.Task(slow_operation(future)) +loop.run_until_complete(future) +print(future.result()) +loop.close() +[out] +_program.py: In function "slow_operation": +_program.py, line 7: Argument 1 to "set_result" of "Future" has incompatible type "int"; expected "str" + + +[case testErrorUsingDifferentFutureType] +import asyncio +from asyncio import Future + +@asyncio.coroutine +def slow_operation(future: 'Future[int]') -> 'Future[None]': + yield from asyncio.sleep(1) + future.set_result(42) + +loop = asyncio.get_event_loop() +future = asyncio.Future() # type: Future[str] +asyncio.Task(slow_operation(future)) # Error +loop.run_until_complete(future) +print(future.result()) +loop.close() +[out] +_program.py, line 11: Argument 1 to "slow_operation" has incompatible type Future[str]; expected Future[int] + +[case testErrorUsingDifferentFutureTypeAndSetFutureDifferentInternalType] +import asyncio +from asyncio import Future + +asyncio.coroutine +def slow_operation(future: 'Future[int]') -> 'Future[None]': + yield from asyncio.sleep(1) + future.set_result('42') #Try to set an str as result to a Future[int] + +loop = asyncio.get_event_loop() +future = asyncio.Future() # type: Future[str] +asyncio.Task(slow_operation(future)) # Error +loop.run_until_complete(future) +print(future.result()) +loop.close() +[out] +_program.py: In function "slow_operation": +_program.py, line 7: Argument 1 to "set_result" of "Future" has incompatible type "str"; expected "int" +_program.py: At top level: +_program.py, line 11: Argument 1 to "slow_operation" has incompatible type Future[str]; expected Future[int] + +[case testErrorSettingCallbackWithDifferentFutureType] +import typing +import asyncio +from asyncio import Future, AbstractEventLoop + +@asyncio.coroutine +def slow_operation(future: 'Future[str]') -> 'Future[None]': + yield from asyncio.sleep(1) + future.set_result('Future is done!') + +def got_result(future: 'Future[int]') -> None: + print(future.result()) + loop.stop() + +loop = asyncio.get_event_loop() # type: AbstractEventLoop +future = asyncio.Future() # type: Future[str] +asyncio.Task(slow_operation(future)) +future.add_done_callback(got_result) # Error + +try: + loop.run_forever() +finally: + loop.close() +[out] +_program.py, line 17: Argument 1 to "add_done_callback" of "Future" has incompatible type Function[[Future[int]] -> None]; expected Function[[Future[str]] -> "Any"] + +[case testErrorOneMoreFutureInReturnType] +import typing +import asyncio +from asyncio import Future + +@asyncio.coroutine +def h4() -> 'Future[Future[int]]': + yield from asyncio.sleep(1) + f = asyncio.Future() #type: Future[int] + return f + +@asyncio.coroutine +def h3() -> 'Future[Future[Future[Future[int]]]]': + x = yield from h4() + x.set_result(42) + f = asyncio.Future() #type: Future[Future[int]] + f.set_result(x) + return f + +@asyncio.coroutine +def h() -> 'Future[None]': + print("Before") + x = yield from h3() + y = yield from x + z = yield from y + print(z) + print(y) + print(x) + +loop = asyncio.get_event_loop() +loop.run_until_complete(h()) +loop.close() +[out] +_program.py: In function "h3": +_program.py, line 17: Incompatible return value type: expected asyncio.futures.Future[asyncio.futures.Future[asyncio.futures.Future[asyncio.futures.Future[builtins.int]]]], got asyncio.futures.Future[asyncio.futures.Future[builtins.int]] + +[case testErrorOneLessFutureInReturnType] +import typing +import asyncio +from asyncio import Future + +@asyncio.coroutine +def h4() -> 'Future[Future[int]]': + yield from asyncio.sleep(1) + f = asyncio.Future() #type: Future[int] + return f + +@asyncio.coroutine +def h3() -> 'Future[Future[int]]': + x = yield from h4() + x.set_result(42) + f = asyncio.Future() #type: Future[Future[int]] + f.set_result(x) + return f + +@asyncio.coroutine +def h() -> 'Future[None]': + print("Before") + x = yield from h3() + y = yield from x + print(y) + print(x) + +loop = asyncio.get_event_loop() +loop.run_until_complete(h()) +loop.close() +[out] +_program.py: In function "h3": +_program.py, line 17: Incompatible return value type: expected asyncio.futures.Future[asyncio.futures.Future[builtins.int]], got asyncio.futures.Future[asyncio.futures.Future[builtins.int]] + +[case testErrorAssignmentDifferentType] +import typing +import asyncio +from asyncio import Future + +class A: + def __init__(self, x: int) -> None: + self.x = x + +class B: + def __init__(self, x: int) -> None: + self.x = x + +@asyncio.coroutine +def h() -> 'Future[None]': + x = yield from future # type: B # Error + print("h: %s" % x.x) + +loop = asyncio.get_event_loop() +future = asyncio.Future() # type: Future[A] +future.set_result(A(42)) +loop.run_until_complete(h()) +loop.close() +[out] +_program.py: In function "h": +_program.py, line 15: Incompatible types in assignment (expression has type "A", variable has type "B") \ No newline at end of file diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index acdecba22e71..42cde428ab72 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -25,7 +25,9 @@ # Files which contain test case descriptions. python_eval_files = ['pythoneval.test', - 'python2eval.test'] + 'python2eval.test'] + +python_34_eval_files = ['pythoneval-asyncio.test'] # Path to Python 3 interpreter python3_path = 'python3' @@ -39,6 +41,10 @@ def cases(self): for f in python_eval_files: c += parse_test_cases(os.path.join(test_data_prefix, f), test_python_evaluation, test_temp_dir, True) + if sys.version_info.major == 3 and sys.version_info.minor >= 4: + for f in python_34_eval_files: + c += parse_test_cases(os.path.join(test_data_prefix, f), + test_python_evaluation, test_temp_dir, True) return c diff --git a/mypy/traverser.py b/mypy/traverser.py index da3e4e31c10e..b686add00a29 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -10,7 +10,7 @@ TryStmt, WithStmt, ParenExpr, MemberExpr, OpExpr, SliceExpr, CastExpr, UnaryExpr, ListExpr, TupleExpr, DictExpr, SetExpr, IndexExpr, GeneratorExpr, ListComprehension, ConditionalExpr, TypeApplication, - FuncExpr, ComparisonExpr, OverloadedFuncDef + FuncExpr, ComparisonExpr, OverloadedFuncDef, YieldFromStmt, YieldFromExpr ) @@ -104,6 +104,10 @@ def visit_yield_stmt(self, o: YieldStmt) -> T: if o.expr is not None: o.expr.accept(self) + def visit_yield_from_stmt(self, o: YieldFromStmt) -> T: + if o.expr is not None: + o.expr.accept(self) + def visit_del_stmt(self, o: DelStmt) -> T: if o.expr is not None: o.expr.accept(self) @@ -146,6 +150,9 @@ def visit_paren_expr(self, o: ParenExpr) -> T: def visit_member_expr(self, o: MemberExpr) -> T: o.expr.accept(self) + def visit_yield_from_expr(self, o: YieldFromExpr) -> T: + o.expr.accept(self) + def visit_call_expr(self, o: CallExpr) -> T: for a in o.args: a.accept(self) diff --git a/mypy/treetransform.py b/mypy/treetransform.py index 8d3113e68b1b..c39692347f51 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -16,7 +16,7 @@ UnicodeExpr, FloatExpr, CallExpr, SuperExpr, MemberExpr, IndexExpr, SliceExpr, OpExpr, UnaryExpr, FuncExpr, TypeApplication, PrintStmt, SymbolTable, RefExpr, UndefinedExpr, TypeVarExpr, DucktypeExpr, - DisjointclassExpr, ComparisonExpr, TempNode + DisjointclassExpr, ComparisonExpr, TempNode, YieldFromStmt, YieldFromExpr ) from mypy.types import Type, FunctionLike from mypy.visitor import NodeVisitor @@ -217,6 +217,9 @@ def visit_assert_stmt(self, node: AssertStmt) -> Node: def visit_yield_stmt(self, node: YieldStmt) -> Node: return YieldStmt(self.node(node.expr)) + def visit_yield_from_stmt(self, node: YieldFromStmt) -> Node: + return YieldFromStmt(self.node(node.expr)) + def visit_del_stmt(self, node: DelStmt) -> Node: return DelStmt(self.node(node.expr)) @@ -301,6 +304,9 @@ def copy_ref(self, new: RefExpr, original: RefExpr) -> None: new.node = target new.is_def = original.is_def + def visit_yield_from_expr(self, node: YieldFromExpr) -> Node: + return YieldFromExpr(self.node(node.expr)) + def visit_call_expr(self, node: CallExpr) -> Node: return CallExpr(self.node(node.callee), self.nodes(node.args), diff --git a/mypy/visitor.py b/mypy/visitor.py index 52f47167d37e..be457fd27d55 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -86,6 +86,9 @@ def visit_assert_stmt(self, o: 'mypy.nodes.AssertStmt') -> T: def visit_yield_stmt(self, o: 'mypy.nodes.YieldStmt') -> T: pass + def visit_yield_from_stmt(self, o: 'mypy.nodes.YieldFromStmt') -> T: + pass + def visit_del_stmt(self, o: 'mypy.nodes.DelStmt') -> T: pass @@ -139,6 +142,9 @@ def visit_name_expr(self, o: 'mypy.nodes.NameExpr') -> T: def visit_member_expr(self, o: 'mypy.nodes.MemberExpr') -> T: pass + def visit_yield_from_expr(self, o: 'mypy.nodes.YieldFromExpr') -> T: + pass + def visit_call_expr(self, o: 'mypy.nodes.CallExpr') -> T: pass diff --git a/pinfer/p.py b/pinfer/p.py old mode 100755 new mode 100644 diff --git a/scripts/mypy b/scripts/mypy old mode 100755 new mode 100644 diff --git a/stubs/3.4/asyncio/__init__.py b/stubs/3.4/asyncio/__init__.py index cafecf5155ce..98d3092224a9 100644 --- a/stubs/3.4/asyncio/__init__.py +++ b/stubs/3.4/asyncio/__init__.py @@ -1,7 +1,9 @@ """The asyncio package, tracking PEP 3156.""" -from asyncio.futures import * -from asyncio.tasks import * -from asyncio.events import * +from asyncio.futures import Future +from asyncio.tasks import (coroutine, sleep, Task, FIRST_COMPLETED, + FIRST_EXCEPTION, ALL_COMPLETED, wait, wait_for) +from asyncio.events import (AbstractEventLoopPolicy, AbstractEventLoop, + Handle, get_event_loop) __all__ = (futures.__all__, tasks.__all__, diff --git a/stubs/3.4/asyncio/events.py b/stubs/3.4/asyncio/events.py index f6007fcc94b5..8a1be6cfdfac 100644 --- a/stubs/3.4/asyncio/events.py +++ b/stubs/3.4/asyncio/events.py @@ -1,21 +1,24 @@ -from typing import Any, typevar, List, Function, Tuple, Union, Dict +from typing import Any, typevar, List, Function, Tuple, Union, Dict, Undefined from abc import ABCMeta, abstractmethod from asyncio.futures import Future -import socket, subprocess -# __all__ = ['AbstractEventLoopPolicy', -# 'AbstractEventLoop', 'AbstractServer', -# 'Handle', 'TimerHandle', +# __all__ = ['AbstractServer', +# 'TimerHandle', # 'get_event_loop_policy', 'set_event_loop_policy', -# 'get_event_loop', 'set_event_loop', 'new_event_loop', +# 'set_event_loop', 'new_event_loop', # 'get_child_watcher', 'set_child_watcher', # ] -__all__ = ['AbstractEventLoop', 'Handle', 'get_event_loop'] +__all__ = ['AbstractEventLoopPolicy', 'AbstractEventLoop', 'Handle', 'get_event_loop'] _T = typevar('_T') +PIPE = Undefined(Any) # from subprocess.PIPE + +AF_UNSPEC = 0 # from socket +AI_PASSIVE = 0 + class Handle: __slots__ = [] # type: List[str] _cancelled = False @@ -58,36 +61,36 @@ def set_default_executor(self, executor: Any) -> None: pass # Network I/O methods returning Futures. @abstractmethod def getaddrinfo(self, host: str, port: int, *, - family: int=0, type: int=0, proto: int=0, flags: int=0) -> List[Tuple[int, int, int, str, tuple]]: pass + family: int = 0, type: int = 0, proto: int = 0, flags: int = 0) -> List[Tuple[int, int, int, str, tuple]]: pass @abstractmethod - def getnameinfo(self, sockaddr: tuple, flags: int=0) -> Tuple[str, int]: pass + def getnameinfo(self, sockaddr: tuple, flags: int = 0) -> Tuple[str, int]: pass @abstractmethod - def create_connection(self, protocol_factory: Any, host: str=None, port: int=None, *, - ssl: Any=None, family: int=0, proto: int=0, flags: int=0, sock: Any=None, - local_addr: str=None, server_hostname: str=None) -> tuple: pass + def create_connection(self, protocol_factory: Any, host: str = None, port: int = None, *, + ssl: Any = None, family: int = 0, proto: int = 0, flags: int = 0, sock: Any = None, + local_addr: str = None, server_hostname: str = None) -> tuple: pass # ?? check Any # return (Transport, Protocol) @abstractmethod - def create_server(self, protocol_factory: Any, host: str=None, port: int=None, *, - family: int=socket.AF_UNSPEC, flags: int=socket.AI_PASSIVE, - sock: Any=None, backlog: int=100, ssl: Any=None, reuse_address: Any=None) -> Any: pass + def create_server(self, protocol_factory: Any, host: str = None, port: int = None, *, + family: int = AF_UNSPEC, flags: int = AI_PASSIVE, + sock: Any = None, backlog: int = 100, ssl: Any = None, reuse_address: Any = None) -> Any: pass # ?? check Any # return Server @abstractmethod def create_unix_connection(self, protocol_factory: Any, path: str, *, - ssl: Any=None, sock: Any=None, - server_hostname: str=None) -> tuple: pass + ssl: Any = None, sock: Any = None, + server_hostname: str = None) -> tuple: pass # ?? check Any # return tuple(Transport, Protocol) @abstractmethod def create_unix_server(self, protocol_factory: Any, path: str, *, - sock: Any=None, backlog: int=100, ssl: Any=None) -> Any: pass + sock: Any = None, backlog: int = 100, ssl: Any = None) -> Any: pass # ?? check Any # return Server @abstractmethod def create_datagram_endpoint(self, protocol_factory: Any, - local_addr: str=None, remote_addr: str=None, *, - family: int=0, proto: int=0, flags: int=0) -> tuple: pass + local_addr: str = None, remote_addr: str = None, *, + family: int = 0, proto: int = 0, flags: int = 0) -> tuple: pass #?? check Any # return (Transport, Protocol) # Pipes and subprocesses. @@ -100,14 +103,14 @@ def connect_write_pipe(self, protocol_factory: Any, pipe: Any) -> tuple: pass #?? check Any # return (Transport, Protocol) @abstractmethod - def subprocess_shell(self, protocol_factory: Any, cmd: Union[bytes,str], *, stdin: Any=subprocess.PIPE, - stdout: Any=subprocess.PIPE, stderr: Any=subprocess.PIPE, + def subprocess_shell(self, protocol_factory: Any, cmd: Union[bytes, str], *, stdin: Any = PIPE, + stdout: Any = PIPE, stderr: Any = PIPE, **kwargs: Dict[str, Any]) -> tuple: pass #?? check Any # return (Transport, Protocol) @abstractmethod - def subprocess_exec(self, protocol_factory: Any, *args: List[Any], stdin: Any=subprocess.PIPE, - stdout: Any=subprocess.PIPE, stderr: Any=subprocess.PIPE, + def subprocess_exec(self, protocol_factory: Any, *args: List[Any], stdin: Any = PIPE, + stdout: Any = PIPE, stderr: Any = PIPE, **kwargs: Dict[str, Any]) -> tuple: pass #?? check Any # return (Transport, Protocol) @@ -146,5 +149,24 @@ def get_debug(self) -> bool: pass @abstractmethod def set_debug(self, enabled: bool) -> None: pass +class AbstractEventLoopPolicy(metaclass=ABCMeta): + @abstractmethod + def get_event_loop(self) -> AbstractEventLoop: pass + @abstractmethod + def set_event_loop(self, loop: AbstractEventLoop): pass + @abstractmethod + def new_event_loop(self) -> Any: pass # return selector_events.BaseSelectorEventLoop + # Child processes handling (Unix only). + @abstractmethod + def get_child_watcher(self) -> Any: pass # return unix_events.AbstractChildWatcher + @abstractmethod + def set_child_watcher(self, watcher: Any) -> None: pass # gen unix_events.AbstractChildWatcher + +class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy): + def __init__(self) -> None: pass + def get_event_loop(self) -> AbstractEventLoop: pass + def set_event_loop(self, loop: AbstractEventLoop): pass + def new_event_loop(self) -> Any: pass # Same return than AbstractEventLoop + def get_event_loop() -> AbstractEventLoop: pass diff --git a/stubs/3.4/asyncio/futures.py b/stubs/3.4/asyncio/futures.py index d1c318d15f85..b08e3430bfd8 100644 --- a/stubs/3.4/asyncio/futures.py +++ b/stubs/3.4/asyncio/futures.py @@ -1,8 +1,8 @@ -from typing import Any, Function, typevar, Generic, List +from typing import Any, Function, typevar, List, Generic, Iterable, Iterator from asyncio.events import AbstractEventLoop # __all__ = ['CancelledError', 'TimeoutError', # 'InvalidStateError', -# 'Future', 'wrap_future', +# 'wrap_future', # ] __all__ = ['Future'] @@ -17,13 +17,13 @@ def activate(self) -> None: pass def clear(self) -> None: pass def __del__(self) -> None: pass -class Future(Generic[_T]): +class Future(Iterator[_T], Generic[_T]): _state = '' _exception = Any #Exception _blocking = False _log_traceback = False _tb_logger = _TracebackLogger - def __init__(self, loop: AbstractEventLoop) -> None: pass + def __init__(self, *, loop: AbstractEventLoop = None) -> None: pass def __repr__(self) -> str: pass def __del__(self) -> None: pass def cancel(self) -> bool: pass @@ -32,9 +32,10 @@ def cancelled(self) -> bool: pass def done(self) -> bool: pass def result(self) -> _T: pass def exception(self) -> Any: pass - def add_done_callback(self, fn: Function[[],Any]) -> None: pass - def remove_done_callback(self, fn: Function[[], Any]) -> int: pass + def add_done_callback(self, fn: Function[[Future[_T]], Any]) -> None: pass + def remove_done_callback(self, fn: Function[[Future[_T]], Any]) -> int: pass def set_result(self, result: _T) -> None: pass def set_exception(self, exception: Any) -> None: pass def _copy_state(self, other: Any) -> None: pass - def __iter__(self) -> Any: pass + def __iter__(self) -> Iterator[_T]: pass + def __next__(self) -> _T: pass diff --git a/stubs/3.4/asyncio/tasks.py b/stubs/3.4/asyncio/tasks.py index c05afcf88ada..6574454c8667 100644 --- a/stubs/3.4/asyncio/tasks.py +++ b/stubs/3.4/asyncio/tasks.py @@ -1,14 +1,38 @@ -from typing import Any, typevar +from typing import Any, Iterable, typevar, Set, Dict, List, TextIO, Union, Tuple, Generic, Function from asyncio.events import AbstractEventLoop -# __all__ = ['coroutine', 'Task', -# 'iscoroutinefunction', 'iscoroutine', -# 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED', -# 'wait', 'wait_for', 'as_completed', 'sleep', 'async', +from asyncio.futures import Future +# __all__ = ['iscoroutinefunction', 'iscoroutine', +# 'as_completed', 'async', # 'gather', 'shield', # ] -__all__ = ['coroutine', 'sleep'] +__all__ = ['coroutine', 'Task', 'sleep', + 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED', + 'wait', 'wait_for'] +FIRST_EXCEPTION = 'FIRST_EXCEPTION' +FIRST_COMPLETED = 'FIRST_COMPLETED' +ALL_COMPLETED = 'ALL_COMPLETED' _T = typevar('_T') -def coroutine(f: Any) -> Any: pass -def sleep(delay: float, result: _T=None, loop: AbstractEventLoop=None) -> _T: pass +def coroutine(f: _T) -> _T: pass # Here comes and go a function +def sleep(delay: float, result: _T = None, loop: AbstractEventLoop = None) -> Future[_T]: pass +def wait(fs: List[Task[_T]], *, loop: AbstractEventLoop = None, + timeout: float = None, return_when: str = ALL_COMPLETED) -> Future[Tuple[Set[Future[_T]], Set[Future[_T]]]]: pass +def wait_for(fut: Future[_T], timeout: float, *, loop: AbstractEventLoop = None) -> Future[_T]: pass + + +class Task(Future[_T], Generic[_T]): + _all_tasks = None # type: Set[Task] + _current_tasks = {} # type: Dict[AbstractEventLoop, Task] + @classmethod + def current_task(cls, loop: AbstractEventLoop = None) -> Task: pass + @classmethod + def all_tasks(cls, loop: AbstractEventLoop = None) -> Set[Task]: pass + def __init__(self, coro: Future[_T], *, loop: AbstractEventLoop = None) -> None: pass + def __repr__(self) -> str: pass + def get_stack(self, *, limit: int = None) -> List[Any]: pass # return List[stackframe] + def print_stack(self, *, limit: int = None, file: TextIO = None) -> None: pass + def cancel(self) -> bool: pass + def _step(self, value: Any = None, exc: Exception = None) -> None: pass + def _wakeup(self, future: Future[Any]) -> None: pass +