-
Notifications
You must be signed in to change notification settings - Fork 6k
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
[python] asyncio support #6245
[python] asyncio support #6245
Conversation
.gitignore
Outdated
@@ -164,3 +164,4 @@ samples/client/petstore/typescript-node/npm/npm-debug.log | |||
# aspnetcore | |||
samples/server/petstore/aspnetcore/.vs/ | |||
effective.pom | |||
\? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when running run-in-docker.sh, this directory continued to get populated with jars.
@@ -202,6 +209,16 @@ public void processOpts() { | |||
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); | |||
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); | |||
supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml")); | |||
|
|||
if ("asyncio".equals(getLibrary())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to replicate behavior I saw in the java client, where you can choose the library and the subdirectory seemingly populated from the resource directory with the same name.
If there's a better pattern here, I'd love to know.
# prerequisite: setuptools | ||
# http://pypi.python.org/pypi/setuptools | ||
|
||
REQUIRES = ["aiohttp", "urllib3", "six >= 1.10", "certifi", "python-dateutil"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aside from the rest.py, the other changes were fairly minor to the files themselves. I'm happy to switch this over to a template conditional for the other cases.
Some notes:
|
We've released 2.2.3 about 2 weeks ago and latest master is now 2.3.0 (I've updated the PR to target master) |
Travis CI failed with the following errors:
Please update the following files to fix it: |
@toumorokoshi I thought the main point of using asyncio would be an async implementation, so users can work with the API client asynchronously, but the implementation ends up to be synchronous due to Also, the |
@frol the modification I did enables asynchronous programming with asyncio. The pattern I implemented was the coroutine-based pattern, rather than callback. For asyncio, it's the more common programming abstraction. https://docs.python.org/3/library/asyncio-task.html async/await functions are still asynchronous, in the sense that I/O tasks do not block the thread of execution. Instead, they return control back to the main event loop. In the context of Asyncio, asynchronous I/O is the expectation, rather than a completely asynchronous processing pipeline. coroutines return futures, and are able to still provide the parallelism one would expect for any callback-based functionality, such as chaining callbacks: https://docs.python.org/3/library/asyncio-task.html#asyncio.Future.add_done_callback or executing tasks in parallel: https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks re the template: sounds good, I can make that change. |
Well, everything you say is true, but it sounds like you think about the use cases for the non-async code base while I think about async usage: async def my_async_func():
pets = pets_api.get_pets() # Async non-blocking call
# Do something else here while the request is processed asynchronously.
for pet in await pets:
print(pet) |
@frol that pattern you're referring to is possible with coroutines: it doesn't really change the behavior. Here's working example code, showing that the pet coroutine starts before the work is done:
prints:
Please note the comments: it explains some nuances around asyncio that you'll run into, regardless of your choice of abstraction. |
@toumorokoshi I haven't worked with asyncio code-bases, but the proposed snippet looks overcomplicated to me. We end up with asyncio implementation which is neither native for "classic" Python nor for asyncio. Is there any benefit in it comparing to the import asyncio
from petstore_api.apis.pet_api import PetApi
pet_api = PetApi()
async def test_api():
loop = asyncio.get_event_loop()
pet = loop.run_in_executor(None, pet_api.get_pet_by_id, 3)
await asyncio.sleep(0.1)
# insert CPU-intensive work here.
print("do work")
pet_result = await pet
print(pet_result)
loop = asyncio.get_event_loop()
loop.run_until_complete(test_api()) P.S. As I mentioned, I don't have much experience with asyncio, so my judgment can be irrelevant. I know that |
@wing328 your example is uncommon for asyncio usage. Applications that utilize asyncio tend to not run anything in a threadpool of any kind. This is because in Python, unlike some languages like Java, threading does not result in taking advantage of multiple CPUs. This is because Python has a Global Interpreter Lock. Thus, multithreading only improves the performance of native c functions bound to Python, and for performing I/O in parallel. asyncio enables non-blocking I/O within a single thread. As such, most asyncio applications utilize no threading whatsoever, and use multiprocessing to consume multiple CPUs. If someone did implement that code as written, it's an anti-pattern for performant asyncio. threads consume a significantly larger amount of memory than asyncio's lightweight threads. In addition, the context switches of threads is much more expensive than asyncio. ensure_future effectively accomplishes the asyncio equivalent to run_in_executor: begins the future, which starts the I/O, and returns control back to the event loop to continue execution of the code at hand. I appreciate the feedback! But as you mentioned, I think you're thinking of use cases for asyncio as someone who doesn't have a lot of experience with it, and is coming from a parallelism-by-threading background. I'm pretty confident in my understand of asyncio's best practices (I'm pretty invested in it), so I hope you'll defer to my expertise in this case. |
@toumorokoshi I think you meant to tag @frol in your reply. |
woops! My apologies. I must have been looking at the wrong place. Thank you for correcting! |
@toumorokoshi that's ok. Btw, very good discussion 👍 |
clientCodegen.setPackageUrl(PythonClientOptionsProvider.PACKAGE_URL_VALUE); | ||
// clientCodegen.setLibrary(PythonClientCodegen.DEFAULT_LIBRARY); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this line stay here?
OK, I don't have any other comments, let's merge |
Looks good to me! |
@frol thanks for reviewing the change. @toumorokoshi thanks for the enhancement. |
FYI. I've sent the following tweet to promote the new feature: |
When is this PR coming to a released version of codegen? |
PR checklist
./bin/
to update Petstore sample so that CIs can verify the change. (For instance, only need to run./bin/{LANG}-petstore.sh
and./bin/security/{LANG}-petstore.sh
if updating the {LANG} (e.g. php, ruby, python, etc) code generator or {LANG} client's mustache templates). Windows batch files can be found in.\bin\windows\
.3.0.0
branch for breaking (non-backward compatible) changes.Description of the PR
This is a pr for #5939. It introduces library variants for the Python client, and adds the 'asyncio' client.
The asyncio client is strictly Python3.5+ only.
@wing328 @frol @masterandrey