-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Yield from #367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Yield from #367
Conversation
Files changed: nodes.py -> - Added YieldFromStmt, extends from YieldStmt traverser.py -> - Add import YieldFromStmt - Added visit_yield_from function visitor.py -> - Added visit_yield_from function parse.py -> - Add import YieldFromStmt - Modified parse_yield_stmt to allow yield from statements (here comes when there is not assigntment, just a yield from wait) - Added parse_yield_from_expr to allow a yield from assigned to a var, return the callExpr of the yield from - Modified parse_expresssion, changed the self.current() to t (there is no reason to call it when saved it before) and added a clausule that checks that a assignment expression can go through yield from TODO: tests (I made "by hand" but it is not automated)
…t's not yield from
…t's not yield from
======== "yield lambda" error correct ------- **parse.py** -> - Modify visit_yield: Check if the next token is "from" before to "expect" it. "Can't yield from a NameExpr" error correct -------- **checkexpr.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **icode.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **output.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **pprinter.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **semanal.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **stats.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **strconv.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **transform.py** -> - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one. **traverser.py** -> - Add NameExpr to import - Check if expression of yield from is a CallFunc or a NameExpr and visit the correct one.
Thanks for the pull request! I'll give it a spin as soon as I have some time. |
Thank you for this awesome project :) No problem, is a long PR with so many changes, I tried to explain all in the text, I hope that I did well, my english is not awesome. The essential commit are 873b1e5, the 5 before this is because I didn't know how to merge all commits in one u.U |
Sorry @rockneurotiko , I've been busy responding to all the issues and finishing the corrections to my PhD dissertation (and also recovering from a cold) so I haven't been able to review your PR. |
I understand @JukkaL , I'm seeing all the mails in python-ideas and the new issues & PR. With all that pep-8 merges, this can't be merged "cleanly". If you want, when have time, check the theory (the text of the PR) and the commit 873b1e5 and if you give me the approval to the idea and/or code (or some changes), I clean the PR merging from the master and do a new clean PR (or maybe add a commit to that one). I don't know if I explain myself, I don't have so good english XD Good luck with your PhD and cold :) |
I went through the new commits. Looks pretty good! There were a bunch of mostly minor things -- see above for detailed comments. |
@rockneurotiko Are you interested in continuing work on this PR? It's almost there and |
@JukkaL Yeah, I'm interested in continuing this PR, I'm sorry for the lack of news, I'm in a new work and I have some familiar problems. I couldn't work in the code, but I readed all the comments and thought about it, the most I think is the way I tried to do the Futures, I will write and ask you about it today or tomorrow, and answer the comments that need it. Do I write the ask and idea here or I write an email? P.S: I'm sorry again. |
No problem, don't push yourself too hard :-) Feel free to write here or send me an email, either way works for me. |
I write here because I writed it in warkdown. (Again, sorry for my bad english, feel free to ask anything) Here is what I thought about the Futures this days. How is nowI started to think and write the add of "yield from" to mypy thinking primary in asyncio support, and the "Futures/Promises" use that this module make it easy to use. It's needed to say that I'm learning some other languages for fun, languages like like Scala or Haskell, and when I thinked how to use the Futures in the type definition, the Scala's Futures or the Haskell's Promises ways comes to my mind (More the Scala's way, because the syntax is similar to mypy's syntax) With that, here is an example that code that I thinked (and with the actual implementation works): import asyncio
from asyncio import Future
@asyncio.coroutine
def compute(x: int, y: int) -> 'Future[int]':
"""
That function will return a int, but can be "yielded from", so
the type is Future[int]
The return type (int) will be wrapped into a Future.
"""
print("Compute %s + %s ..." % (x, y))
yield from asyncio.sleep(1.0)
return x + y # Here the int is wrapped in Future[int]
@asyncio.coroutine
def print_sum(x: int, y: int) -> 'Future[None]':
"""
Don't return nothing, but can be "yielded from", so is a Future.
"""
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() Here is some notes about the code and implementation:
But this comes with some things that I had to fix:
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[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()
What to do?
Personally, I like how is now, seems intuitive to me, for iterators and for futures. |
Thanks for the update. I've been busy the past couple of days, but I should have time to respond later this week. |
Sorry for the slow progress. I should have more time this weekend. |
Looks pretty good! Again, apologies for the slow progress. The last month has been one of the busiest I've ever had... Some comments below.
|
Hi @JukkaL ! I'm sorry for late reply, I'm so busy too. I think that next week I will can do this, after the PyCON Spain :-) About the comments:
|
Modified some things to satisfy travis and typecheck. Removed the examples in stubs/3.4/asyncio, now are tests in mypy/tests/data/pythoneval-asyncio.test
Hi @JukkaL After a long day, I've made the merge with the actual master, created full tests for asyncio (only run in python >= 3.4) and removed the examples from the stubs. I have to write the general samples, but right now can be merged and works all :-) |
Cool, thanks for making the updates! Now it's looking much better :-) |
This is a text to explain how yield from works, and why I took some decisions :-)
(In the commits, are so much "white lines" changed, that's because in the original source code are lines with just spaces, or spaces at the end of the line, and my editor (Subl3) remove that)
Yield From
yield from can be applied to Iterables and Futures (A kind of Iterable).
And can be applied in two ways:
About Iterables:
Statement:
Check if the return type of the function is an iterable and the infered of the iterable applied is compatible with the return type.
Ex:
In the example above, the function return a Iterator of ints, that is a supertype of Iterable.
Here another example with a function Iterator:
Expression:
Right now just checks if the applied is an Iterable.
In the future, when other things are added to mypy, here should check the
return type, and check the types (just like in statement).
The things that mypy needs to add this to the expression type check:
This two things have interaction with the new send(), close() and throw() method of the iterables (this should have added too)
About Futures.
I said that Futures are a "kind" of iterables, because it have an iter function but we never iterate over there (we just can do one next(), if try to do any more raises an error). The main loop will catch the futures that had been "yielded from" or that are inserted directly (call_soon, run_until_complete, ...)
So, the idea that I had to write the Future type, and that I've implemented is that one:
So, if "yield from" is applied to a Future, we won't get it (the loop will).
But if we wan't to do a function that can be "yielded from" (this is called coroutine), then the function need to have the return type as Future[Some], and the @asyncio.coroutine decorator (this is explained after)
Statement:
We just don't care about it, we won't get the Future, the loop will, so we don't need to check anything.
Ex:
We set the return type as Future[int] because we want to let know that this is a coroutine and can be yielded from but, also, that will return an int.
If you do: "y = yield from h()", the program will sleep 2 seconds, and then y will be 1 (the return value).
Actually, if you just call the function, and not "yield from" it, you will have a generator (but can't iterate over there, that raises an exception)
Expression:
When we check the expression yield from, if we see that the type applied is a Future[Some], then, the type to return (to assignment_stmt) won't be Future[Some], instead of that, we'll return the type Some, because, as said, the Future is just for know where are a coroutine, or where can be "yielded from", but in the assignment expression, the variable get the type returned, not the Future.
Ex:
Other things of Futures
Ex:
For that reason, the type have to be writted as string ( 'Future[int]' ), if it's not writted in that way, the script will fail when try to run it, because the real Future type is not subscritable.
I think that in the future, can be a type in "typing", called Futur or some like that, to can write the type without the string.
Because of that (Future comes from asyncio), now the tests to Futures can't be written (fails trying to import all things)
Anyway, in stubs/3.4/asyncio I added a directory called "examples" where are so much examples about Futures.
You have to do it like if you have a Iterator that return an Iterator, ...:
But with Futures!
Summary
To say that a function can be "yielded from", the return type need to have a Future[Some]
The first Future of the type, is just to indicate that can be yielded from, so, if you
are going to return an int, the type of the function is Future[int] and if you are going to return a Future
that have inside an int, the type of the function is: Future[Future[int]]
The type in the function have to be writted in string.
See all the examples in "stubs/3.4/asyncio/examples"