Articles on McSinyx's Bloghttps://blogs.python-gsoc.orgUpdates on different articles published on McSinyx's BlogenMon, 31 Aug 2020 14:48:23 +0000Outrohttps://blogs.python-gsoc.org/en/mcsinyxs-blog/outro/<div class="documentwrapper"> <div class="bodywrapper"> <div class="body"> <div class="section">Note: This article's HTML source is exported from reST.  Without necessary CSS, some part might look hideous.  Please consider viewing <a href="https://mcsinyx.github.io/gsoc2020/blog20200831.html">on my personal blog</a>. <div class="section"> <h2>The Look</h2> <p>At the time of writing, <a class="reference external" href="https://github.com/pypa/pip/pull/8771">implementation-wise parallel download is ready</a>:</p> <a href="https://asciinema.org/a/356704"><img alt="" src="https://asciinema.org/a/356704.svg"></a> <p>Does this mean I’ve finished everything just-in-time? This sounds to good to be true! And how does it perform? Welp…</p> </div> <div class="section"> <h2>The Benchmark</h2> <p>Here comes the bad news: under a decent connection to the package index, using <code class="docutils literal notranslate"><span class="pre">fast-deps</span></code> does not make <code class="docutils literal notranslate"><span class="pre">pip</span></code> faster. For best comparison, I will time <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">download</span></code> on the following cases:</p> <div class="section"> <h3>Average Distribution</h3> <p>For convenience purposes, let’s refer to the commands to be used as follows</p> <dl class="simple"> <dt>legacy-resolver</dt> <dd> <p><code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">--no-cache-dir</span> <span class="pre">download</span> <span class="pre">{requirement}</span></code></p> </dd> <dt>2020-resolver</dt> <dd> <p><code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">--use-feature=2020-resolver</span> <span class="pre">--no-cache-dir</span> <span class="pre">download</span> <span class="pre">{requirement}</span></code></p> </dd> <dt>fast-deps</dt> <dd> <p><code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">--use-feature=2020-resolver</span> <span class="pre">--use-feature=fast-deps</span> <span class="pre">--no-cache-dir</span> <span class="pre">download</span> <span class="pre">{requirement}</span></code></p> </dd> </dl> <p>In the first test, I used <a class="reference external" href="https://www.youtube.com/playlist?list=PLAA9fHINq3sayfxEyZSF2D_rMgDZGyL3N">axuy</a> and obtained the following results</p> <table class="docutils align-default"> <tbody> <tr class="row-odd"> <th class="head"> <p>legacy-resolver</p> </th> <th class="head"> <p>2020-resolver</p> </th> <th class="head"> <p>fast-deps</p> </th> </tr> </tbody> <tbody> <tr class="row-even"> <td> <p>7.709s</p> </td> <td> <p>7.888s</p> </td> <td> <p>10.993s</p> </td> </tr> <tr class="row-odd"> <td> <p>7.068s</p> </td> <td> <p>7.127s</p> </td> <td> <p>11.103s</p> </td> </tr> <tr class="row-even"> <td> <p>8.556s</p> </td> <td> <p>6.972s</p> </td> <td> <p>10.496s</p> </td> </tr> </tbody> </table> <p>Funny enough, running <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">download</span></code> with <code class="docutils literal notranslate"><span class="pre">fast-deps</span></code> in a directory with downloaded files already took around 7-8 seconds. This is because to lazily download a wheel, <code class="docutils literal notranslate"><span class="pre">pip</span></code> has to <a class="reference external" href="https://github.com/pypa/pip/pull/8670">make many requests</a> which are apparently more expensive than actual data transmission on my network.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>With unstable connection to PyPI (for some reason I am not confident enough to state), this is what I got</p> <table class="docutils align-default"> <tbody> <tr class="row-odd"> <th class="head"> <p>2020-resolver</p> </th> <th class="head"> <p>fast-deps</p> </th> </tr> </tbody> <tbody> <tr class="row-even"> <td> <p>1m16.134s</p> </td> <td> <p>0m54.894s</p> </td> </tr> <tr class="row-odd"> <td> <p>1m0.384s</p> </td> <td> <p>0m40.753s</p> </td> </tr> <tr class="row-even"> <td> <p>0m50.102s</p> </td> <td> <p>0m41.988s</p> </td> </tr> </tbody> </table> <p>As the connection was <em>unstable</em> and that the majority of <code class="docutils literal notranslate"><span class="pre">pip</span></code> networking is performed as CI/CD with large and stable bandwidth, I am unsure what this result is supposed to tell (-;</p> </div> </div> <div class="section"> <h3>Large Distribution</h3> <p>In this test, I used <a class="reference external" href="https://www.tensorflow.org/">TensorFlow</a> as the requirement and obtained the following figures:</p> <table class="docutils align-default"> <tbody> <tr class="row-odd"> <th class="head"> <p>legacy-resolver</p> </th> <th class="head"> <p>2020-resolver</p> </th> <th class="head"> <p>fast-deps</p> </th> </tr> </tbody> <tbody> <tr class="row-even"> <td> <p>0m52.135s</p> </td> <td> <p>0m58.809s</p> </td> <td> <p>1m5.649s</p> </td> </tr> <tr class="row-odd"> <td> <p>0m50.641s</p> </td> <td> <p>1m14.896s</p> </td> <td> <p>1m28.168s</p> </td> </tr> <tr class="row-even"> <td> <p>0m49.691s</p> </td> <td> <p>1m5.633s</p> </td> <td> <p>1m22.131s</p> </td> </tr> </tbody> </table> </div> <div class="section"> <h3>Distribution with Conflicting Dependencies</h3> <p>Some requirement that will trigger a decent amount of backtracking by the current implementation of the new resolver <code class="docutils literal notranslate"><span class="pre">oslo-utils==1.4.0</span></code>:</p> <table class="docutils align-default"> <tbody> <tr class="row-odd"> <th class="head"> <p>2020-resolver</p> </th> <th class="head"> <p>fast-deps</p> </th> </tr> </tbody> <tbody> <tr class="row-even"> <td> <p>14.497s</p> </td> <td> <p>24.010s</p> </td> </tr> <tr class="row-odd"> <td> <p>17.680s</p> </td> <td> <p>28.884s</p> </td> </tr> <tr class="row-even"> <td> <p>16.541s</p> </td> <td> <p>26.333s</p> </td> </tr> </tbody> </table> </div> </div> <div class="section"> <h2>What Now?</h2> <p>I don’t know, to be honest. At this point I’m feeling I’ve failed my own (and that of other stakeholders of <code class="docutils literal notranslate"><span class="pre">pip</span></code>) expectation and wasted the time and effort of <code class="docutils literal notranslate"><span class="pre">pip</span></code>’s maintainers reviewing dozens of PRs I’ve made in the last three months.</p> <p>On the bright side, this has been an opportunity for me to explore the codebase of package manager and discovered various edge cases where the new resolver has yet to cover (e.g. I’ve just noticed that <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">download</span></code> would save to-be-discarded distributions, I’ll file an issue on that soon). Plus I got to know many new and cool people and idea, which make me a more helpful individual to work on Python packaging in the future, I hope.</p> </div> </div> </div> </div> </div>vn.mcsinyx@gmail.com (McSinyx)Mon, 31 Aug 2020 14:48:23 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/outro/Final Check-Inhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/final-check-in/<p>Hello there!</p> <h2>What did I do last week?</h2> <p> Not much, but seemingly implementation-wise I have finished my GSoC project: </p> <ul> <li> Finish the implementation of wheels' parallel download (<a href="https://github.com/pypa/pip/pull/8771">GH-8771</a>) </li> <li> Help make <code>pip</code>'s CI green again (<a href="https://github.com/pypa/pip/pull/8790">GH-8790</a>) </li> <li> Reformat a few spots in user guide (<a href="https://github.com/pypa/pip/pull/8795">GH-8795</a>) </li> </ul> <h2>Did I get stuck anywhere?</h2> <p>I got sick, but I am recovering now!</p> <h2>What is coming up next?</h2> <p> I will try to spend the time I got left within the scope of GSoC to <a href="https://github.com/pypa/pip/issues/8720">improve cache usage of the <code>fast-deps</code> feature</a>. </p>vn.mcsinyx@gmail.com (McSinyx)Mon, 24 Aug 2020 15:14:05 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/final-check-in/Parallelizing Wheel Downloadshttps://blogs.python-gsoc.org/en/mcsinyxs-blog/parallelizing-wheel-downloads/<p><em> And now it's clear as this promise<br> That we're making<br> Two progress bars into one<br> </em></p> <p> Hello there! It has been raining a lot lately and my wisdom tooth has decided to start growing today, causing me a mild fever. To whoever reading this, I hope it wouldn't happen to you. </p> <h2>Download Parallelization</h2> <p> I've been working on <code>pip</code>'s download parallelization for quite a while now. As distribution download in <code>pip</code> was modeled as a lazily evaluated iterable of chunks, parallelizing such procedure is as simple as submitting routines that write files to disk to a worker pool. </p> <p> Or at least that is what I thought. </p> <h2>Progress Reporting UI</h2> <p> <code>pip</code> is currently using customly defined progress reporting classes, which was not designed to working with multithreading code. Firstly, I want to try using these instead of defining separate UI for multithreaded progresses. As they use system signals for termination, one must the progress bars has to be running the main thread. Or sort of. </p> <p> Since the progress bars are designed as iterators, I realized that we can call <code>next</code> on them. So quickly, I throw in some queues and locks, and prototyped the first <em>working</em> <a href="https://github.com/pypa/pip/pull/8771/commits/bf98cb7dc2fa2991bac1a2b386fcf828aaf32388">implementation of progress synchronization</a>. </p> <h2>Performance Issues</h2> <p> Welp, I only said that it works, but I didn't mention the performance, which is terrible. I am pretty sure that the slow down is with the synchronization, since the <code>map_multithread</code> call doesn't seem to trigger anything that may introduce any sort of blocking. </p> <p> This seems like a lot of fun, and I hope I'll get better tomorrow to continue playing with it! </p>vn.mcsinyx@gmail.com (McSinyx)Mon, 17 Aug 2020 15:14:09 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/parallelizing-wheel-downloads/Sixth Check-Inhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/sixth-check-in/<p>Hello there!</p> <h2>What did I do last week?</h2> <p>It has been a quite fun week for me, given the current state of development and the newly dicovered bugs thanks to pip 20.2 release:</p> <ul> <li>Initiate discussion with the maintainers of pip on isolating networking code for late download in parallel (<a href="https://github.com/pypa/pip/pull/8697">GH-8697</a>)</li> <li>Discuss the UI of parallel download (<a href="https://github.com/pypa/pip/pull/8698">GH-8698</a>)</li> <li>Log debug information relating lazy wheel decision (<a href="https://github.com/pypa/pip/pull/8710">GH-8710</a>)</li> <li>Disable caching for range requests (<a href="https://github.com/pypa/pip/pull/8716">GH-8716</a>)</li> <li>Dedent late download logs (<a href="https://github.com/pypa/pip/pull/8722">GH-8722</a>)</li> <li>Add a hook for batch downloading (third attempt I think) (<a href="https://github.com/pypa/pip/pull/8737">GH-8737</a>)</li> <li>Test hash checking for fast-deps (<a href="https://github.com/pypa/pip/pull/8743">GH-8743</a>)</li> </ul> <h2>Did I get stuck anywhere?</h2> <p>Not exactly, everything is going smoothly and I'm feeling awesome!</p> <h2>What is coming up next?</h2> <p>I'll try to solve GH-8697 and GH-8698 within the next few days. I am optimistic that the parallel download prototype will be done within this week.</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 10 Aug 2020 16:53:19 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/sixth-check-in/Sorting Things Outhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/sorting-things-out/<p> Hi! I really hope that everyone reading this is still doing okay, and if that isn't the case, I wish you a good day! </p> <h2><code>pip</code> 20.2 Released!</h2> <p> Last Wednesday, <code>pip</code> 20.2 was released, delivering the <code>2020-resolver</code> as well as many other improvements! I was lucky to be able to get the <code>fast-deps</code> feature to be included as part of the release. A brief description of this <em>experimental</em> feature as well as testing instruction can be found on <a href="https://discuss.python.org/t/announcement-pip-20-2-release/4863/2">Python Discuss</a>. </p> <p> The public exposure of the feature also remind me of some further <a href="https://github.com/pypa/pip/pull/8681">optimization</a> to make on <a href="https://github.com/pypa/pip/issues/8670">the lazy wheel</a>. Hopefully without download parallelization it would not be too slow to put off testing by concerned users of <code>pip</code>. </p> <h2>Preparation for Download Parallelization</h2> <p>As of this moment, we already have:</p> <ul> <li> <a href="https://github.com/pypa/pip/pull/8162#issuecomment-667504162"> Multithreading pool fallback working </a> </li> <li> An opt-in to use lazy wheel to optain dependency information, and thus getting a list of wheels at the end of resolution ready to be downloaded together </li> </ul> <p> What's left is <em>only</em> to interject a parallel download somewhere after the dependency resolution step. Still, this struggles me way more than I've ever imagined. I got so stuck that I had to give myself a day off in the middle of the week (and study some Rust), then I came up with <a href="https://github.com/pypa/pip/pull/8638">something what was agreed upon as difficult to maintain</a>. </p> <p> Indeed, a large part of this is my fault, for not communicating the design thoroughly with <code>pip</code>'s maintainers and not carefully noting stuff down during (verbal) discussions with my mentor. Thankfully <a href="https://github.com/pypa/pip/pull/8685">Chris Hunt came to the rescue</a> and did a refactoring that will make my future work much easier and cleaner. </p>vn.mcsinyx@gmail.com (McSinyx)Mon, 03 Aug 2020 18:58:09 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/sorting-things-out/Fifth Check-Inhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/fifth-check-in/<p> Hello and I hope y'all are still doing well! </p> <h2>What did I do last week?</h2> <p> I was not really productive last week—most of the following tickets are fillers to make use of the spare cycles I had when I was still trying to figure out the way to implement the main work. </p> <ul> <li> Finalize the <code>--use-feature=fast-deps</code> flag (<a href="https://github.com/pypa/pip/pull/8588">GH-8588</a>) </li> <li> Improve mocking of environment variables in the test suit (<a href="https://github.com/pypa/pip/pull/8614">GH-8614</a>) </li> <li> Finalize the fix for verbose/quiet options specified via configuration files and environment variables (<a href="https://github.com/pypa/pip/pull/8578">GH-8578</a>) </li> <li> Clean up a tiny bit in the resolver internal API (<a href="https://github.com/pypa/pip/pull/8629">GH-8629</a>) </li> <li> Start working on seperating the download of wheels from dependency resolution (<a href="https://github.com/pypa/pip/pull/8638">GH-8638</a>) </li> </ul> <h2>Did I get stuck anywhere?</h2> <p> I'm struggling on refactoring the code to support separate download. <code>pip</code>'s codebase was not intended for this and thus there are many execution paths and other details entangled around the relevant area. </p> <h2>What is coming up next?</h2> <p> <code>pip</code> 20.2 is going to be released within the next few days with <code>--use-feature=fast-deps</code> included and I'm mentally prepare to fix any undiscovered problem. At the same time, I will continue working on GH-8638 and hopefully get it done soon enough to begin drafting download parallelization strategies, mostly with the UI. </p>vn.mcsinyx@gmail.com (McSinyx)Mon, 27 Jul 2020 17:15:15 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/fifth-check-in/I've Walked 500 Miles…https://blogs.python-gsoc.org/en/mcsinyxs-blog/i-ve-walked-500-miles/<p><em>… and I would walk 500 more<br> Just to be the man who walks a thousand miles<br> To fall down at your door </em></p> <p><img alt="" src="https://gist.github.com/McSinyx/4624ab7af8b36d45bc3bf5612e6a6504/raw/8f68cc0136f837f61bdf6b38848373d48e498a40/500-miles.gif"></p> <h2>The Main Road</h2> <p>Hi, have you met <code>fast-deps</code>? It's (going to be) the name of <code>pip</code>'s experimental feature that may improve the speed of dependency resolution of the new resolver. By avoid downloading whole wheels to just obtain metadata, it is especially helpful when <code>pip</code> has to do heavy backtracking to resolve conflicts.</p> <p>Thanks to <a href="https://github.com/pypa/pip/pull/8532#discussion_r453990728">Chris Hunt's review on GH-8537</a>, my mentor Pradyun Gedam and I worked out a less hacky approach to inteject the call to lazy wheel during the resolution process. A new PR <a href="https://github.com/pypa/pip/pull/8588">GH-8588</a> was filed to implement it—I could have <em>just</em> worked on top of the old PR and rebased, but my <code>git</code> skill is far from gud enough to confidently do it.</p> <p>Testing this one has been a lot of fun though. At first, integration tests were added as a rerun of the tests for the new resolver, with an additional flag to use feature <code>fast-deps</code>. It indeed made me feel guilty towards <a href="https://travis-ci.com/">Travis</a>, who has to work around 30 minutes more every run. Per Chris Hunt's suggestion, in the new PR, I instead write a few functional tests for the area relating the most to the feature, namely <code>pip</code>'s subcommands <code>wheel</code>, <code>download</code> and <code>install</code>.</p> <p>It was also suggested that a mock server with HTTP range requests support might be better (in term of performance and reliablilty) than for testing. However, <a href="https://github.com/pypa/pip/pull/8584#issuecomment-659227702">I have yet to be able to make Werkzeug do it</a>.</p> <p>Why did I say I'm half way there? With the parallel utilities merged and a way to quickly get the list of distribution to be downloaded being really close, what left is <em>only</em> to figure out a way to properly download them in parallel. With no distribution to be added during the download progress, the model of this will fit very well with the architecture in <a href="https://blogs.python-gsoc.org/media/proposals/pip-parallel-dl.pdf">my original proposal</a>. A batch downloader can be implemented to track the progress of each download and thus report them cleanly as e.g. progress bar or percentage. This is the part I am second-most excited about of my GSoC project this summer (after the synchronization of downloads written in my proposal, which was then superseded by <code>fast-deps</code>) and I can't wait to do it!</p> <h2>The Side Quests</h2> <p>As usual, I make sure that I complete every side quest I see during the journey:</p> <ul> <li><a href="https://github.com/pypa/pip/pull/8568">GH-8568</a>: Declare constants in <code>configuration.py</code> as such</li> <li><a href="https://github.com/pypa/pip/pull/8571">GH-8571</a>: Clean up <code>Configuration.unset_value</code> and nit the class' <code>__init__</code></li> <li><a href="https://github.com/pypa/pip/pull/8578">GH-8578</a>: Allow verbose/quite level to be specified via config file and env var</li> <li><a href="https://github.com/pypa/pip/pull/8599">GH-8599</a>: Replace tabs by spaces for consistency</li> </ul> <h2>Snap Back to Reality</h2> <p>A bit about me, I actually walked 500 meters earlier today to a bank and walked 500 more to another to prepare my Visa card for purchasing the upcoming Pinephone prototype. It's one of the first smartphones to fully support a GNU/Linux distribution, where one can run desktop apps (including proper terminals) as well as traditional services like SSH, HTTP server and IPFS node because why not? Just a few hours ago, I pre-ordered the <a href="https://postmarketos.org/blog/2020/07/15/pinephone-ce-preorder/">postmarketOS community edition</a> with additional hardware for convergence.</p> <p>If you did not come here for a Pinephone ad, please take my apologies though d-; and to ones reading this, I hope you all can become the person who walks a thousand miles to fall down at the door opening to all what you ever wished for!</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 20 Jul 2020 18:04:30 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/i-ve-walked-500-miles/Fourth Check-Inhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/fourth-check-in/<p>Hello there! I'm having my second year's last exam tomorrow, but it <a href="https://www.youtube.com/watch?v=F1B9Fk_SgI0">feels like summer</a> already! I've been finalizing quite a few things to get them ready for pip 20.2b2.</p> <h2>What did I do last week?</h2> <p>I've spent most of the time on getting <a href="https://github.com/pypa/pip/pull/8532">the opt-in</a> of obtaining dependency information via lazy wheels ready. It will be available as <code>--use-feature=fast-deps</code> and only has effect when <code>--use-feature=2020-resolver</code> also presents.</p> <p>While waiting for reviews and suggestions, I made some patches for internal cleansing, namely <a href="https://github.com/pypa/pip/pull/8568">GH-8568</a>, <a href="https://github.com/pypa/pip/pull/8571">GH-8571</a> and <a href="https://github.com/pypa/pip/pull/8578">GH-8578</a>. Some of the similar patches I made earlier were also merged last week: <a href="https://github.com/pypa/pip/pull/8456">GH-8456</a> and <a href="https://github.com/pypa/pip/pull/8538">GH-8538</a>.</p> <h2>Did I get stuck anywhere?</h2> <p>Not really, everything was going as expected for me.</p> <h2>What is coming up next?</h2> <p>After <a href="https://github.com/pypa/pip/pull/8532">GH-8532</a>, I'll work on the parallel download of the postponed wheels. My main current concern is with how the download progress will be reported to the users, but I think I'll figure it out soon.</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 13 Jul 2020 10:43:31 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/fourth-check-in/I'm Not Drowning On My Ownhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/i-m-not-drowning-on-my-own/<h2>Cold Water</h2> <p>Hello there! My schoolyear is coming to an end, with some final assignments and group projects left to be done. I for sure underestimated the workload of these and in the last (and probably next) few days I'm drowning in work trying to meet my deadlines.</p> <p>One project that might be remotely relevant is <a href="https://github.com/McSinyx/cheese-shop">cheese-shop</a>, which tries to manage the metadata of packages from the real <a href="https://pypi.org">Cheese Shop</a>. Other than that, schoolwork is draining a lot of my time and I can't remember the last time I came up with something new for my GSoC Project )-;</p> <h2>Warm Water</h2> <p>On the bright side, I received a lot of help and encouragement from contributors and stakeholders of <code>pip</code>. In the last week alone, I had five pull requests merged:</p> <ul> <li><a href="https://github.com/pypa/pip/pull/8332">GH-8332</a>: Add license requirement to <code>_vendor/README.rst</code></li> <li><a href="https://github.com/pypa/pip/pull/8320">GH-8320</a>: Add utilities for parallelization</li> <li><a href="https://github.com/pypa/pip/pull/8504">GH-8504</a>: Parallelize <code>pip list --outdated</code> and <code>--uptodate</code></li> <li><a href="https://github.com/pypa/pip/pull/8411">GH-8411</a>: Refactor operations.prepare.prepare_linked_requirement</li> <li><a href="https://github.com/pypa/pip/pull/8467">GH-8467</a>: Add utitlity to lazily acquire wheel metadata over HTTP</li> </ul> <p>In addition to helping me getting my PRs merged, my mentor Pradyun Gedam also gave me my first official feedback, including what I'm doing right (and wrong too!) and what I should keep doing to increase the chance of the project being successful.</p> <p><a href="https://github.com/pypa/pip/pull/7819">GH-7819</a>'s roadmap (Danny McClanahan's discoveries and works on lazy wheels) is being closely tracked by <code>hatch</code>'s maintainter Ofek Lev, which really makes me proud and warms my heart, that what I'm helping build is actually needed by the community!</p> <h2>Learning How To Swim</h2> <p>With <a href="https://github.com/pypa/pip/pull/8467">GH-8467</a> and <a href="https://github.com/pypa/pip/pull/8530">GH-8530</a> merged, I'm now working on <a href="https://github.com/pypa/pip/pull/8532">GH-8532</a> which aims to roll out the lazy wheel as the way to obtain dependency information via the CLI flag <code>--use-feature=lazy-wheel</code>.</p> <p>GH-8532 was failing initially, despite being relatively trivial and that the commit it used to base on was passing. Surprisingly, after rebasing it on top of GH-8530, it suddenly became green mysteriously. After the first (early) review, I was able to iterate on my earlier code, which used the ambiguous exception <code>RuntimeError</code>.</p> <p>The rest to be done is <em>just</em> adding some functional tests (I'm pretty sure this will be either overwhelming or underwhelming) to make sure that the command-line flag is working correctly. Hopefully this can make it into the beta of the upcoming release <a href="https://github.com/pypa/pip/issues/8511">this month</a>.</p> <p><img alt="" src="https://gist.githubusercontent.com/McSinyx/4624ab7af8b36d45bc3bf5612e6a6504/raw/9813a50b6b533e66a529f2523b517ba9a8aa4847/lazy-wheel.jpg" style="width: 100%;"></p> <p>In other news, I've also submitted <a href="https://github.com/pypa/pip/pull/8538">a patch improving the tests for the parallelization utilities</a>, which was really messy as I wrote them. Better late than never!</p> <p>Metaphors aside, I actually can't swim d-:</p> <h2>Dive Plan</h2> <p>After GH-8532, I think I'll try to parallelize downloads of wheels that are lazily fetched only for metadata. By the current implementation of the new resolver, for <code>pip install</code>, this can be injected directly between the resolution and build/installation process.</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 06 Jul 2020 19:09:15 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/i-m-not-drowning-on-my-own/Third Check-Inhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/third-check-in/<p> Holla, holla, holla! Last seven days has not been a really productive week for me, though I think there are still some nice things to share with you all here! The good news is that I've finish my last leçon as a somophore, the bad news is that I have a bunch of upcoming tests, mainly in the form of group projects and/or presentation (phew!). Enough about me, let's get back to <code>pip</code>: </p> <h2>What did I do last week?</h2> <p>Not much, actually )-:</p> <ul> <li> Write some tests for <a href="https://github.com/pypa/pip/pull/8467">the HTTP range mapping for wheel</a>. </li> <li> <a href="https://github.com/pypa/pip/pull/8504">Try to bring back multithreaded <code>pip list --outdated</code> and <code>--uptodate}</code></a>, as <a href="https://github.com/pypa/pip/pull/8320">the parallel <code>map</code></a> was merged earlier today </li> <li> Nitpick <a href="https://github.com/pypa/pip/pull/8332">GH-8332</a> (yep it's a new low for me to include this to the list (-:). </li> </ul> <h2>Did I get stuck anywhere?</h2> <p> Not exactly, since I didn't do much d-; <a href="https://github.com/pulls?q=is%3Aopen+is%3Apr+author%3AMcSinyx+archived%3Afalse+repo%3Apypa%2Fpip+sort%3Aupdated-desc">Many of my PRs</a> are stalling though. On one hand the maintainers of <code>pip</code> are all volunteers working in there free time, on the other hand I don't think I have tried hard enough to get their attention on my PRs. </p> <h2>What is coming up next?</h2> <p> I'll try my best getting the following merged upstream before <a href="https://github.com/pypa/pip/issues/8206">the upcoming beta release</a>: </p> <ul> <li> Parallel networking for <code>pip list</code>: <a href="https://github.com/pypa/pip/pull/8504">GH-8504</a> </li> <li> Lazy wheel for dependency information: <a href="https://github.com/pypa/pip/pull/8467">GH-8467</a>, <a href="https://github.com/pypa/pip/pull/8411">GH-8411</a> (to determine if hashing is required) and <a href="https://github.com/pypa/pip/pull/8467#issuecomment-648717032">a new patch introducing this as an unstable feature</a> </li> </ul>vn.mcsinyx@gmail.com (McSinyx)Mon, 29 Jun 2020 15:49:58 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/third-check-in/The Wonderful Wizard of O'ziphttps://blogs.python-gsoc.org/en/mcsinyxs-blog/the-wonderful-wizard-of-o-zip/<p><em>Never give up… No one knows what's going to happen next.</em></p> <h2>Preface</h2> <p>Greetings and best wishes! I had a lot of fun during the last week, although admittedly nothing was really finished. In summary, these are the works I carried out in the last seven days:</p> <ul> <li>Finilizing <a href="https://github.com/pypa/pip/pull/8320">utilities for parallelization</a></li> <li><a href="https://github.com/pypa/pip/pull/8467">Continuing experimenting </a> on <a href="https://github.com/pypa/pip/pull/8442"> using lazy wheels for dependency resolution </a></li> <li>Polishing up <a href="https://github.com/pypa/pip/pull/8411">the patch refactoring <code>operations.prepare.prepare_linked_requirement</code></a></li> <li><a href="https://github.com/pypa/pip/pull/8423#issuecomment-645418725">Adding <code>flake8-logging-format</code> to linter </a></li> <li>Splitting <a href="https://github.com/pypa/pip/pull/8456">the linting patch</a> from <a href="https://github.com/pypa/pip/pull/8332">the PR adding the license requirement to vendor README</a></li> </ul> <h2>The <code>multiprocessing[.dummy]</code> wrapper</h2> <p>Yes, you read it right, this is the same section as last fortnight's blog. My mentor Pradyun Gedam gave me a green light to have <a href="https://github.com/pypa/pip/pull/8411">GH-8411</a> merged without support for Python 2 and the non-lazy map variant, which turns out to be troublesome for multithreading.</p> <p>The tests still needs to pass of course and the flaky tests (see <a href="https://github.com/pypa/pip/commits/master">failing tests over Azure Pipeline in the past</a>) really gave me a panic attack earlier today. We probably need to mark them as xfail or investigate why they are undeterministic specifically on Azure, but the real reason I was <em>all caught up and confused</em> was that the unit tests I added mess with the cached imports and as <code>pip</code>'s tests are run in parallel, who knows what it might affect. I was so relieved to not discover any new set of tests made flaky by ones I'm trying to add!</p> <h2>The file-like object mapping ZIP over HTTP</h2> <p>This is where the fun starts. Before we dive in, let's recall some background information on this. As discovered by Danny McClanahan in <a href="https://github.com/pypa/pip/issues/7819">GH-7819</a>, it is possible to only download a potion of a wheel and it's still valid for <code>pip</code> to get the distribution's metadata. In the same thread, Daniel Holth suggested that one may use HTTP range requests to specifically ask for the tail of the wheel, where the ZIP's central directory record as well as where usually <code>dist-info</code> (the directory containing <code>METADATA</code>) can be found.</p> <p>Well, <em>usually</em>. While PEP 427 does indeed recommend</p> <blockquote>Archivers are encouraged to place the <code>.dist-info</code> files physically at the end of the archive. This enables some potentially interesting ZIP tricks including the ability to amend the metadata without rewriting the entire archive.</blockquote> <p>one of the mentioned <em>tricks</em> is adding shared libraries to wheels of extension modules (using e.g. <code>auditwheel</code> or <code>delocate</code>). Thus for non-pure Python wheels, it is unlikely that the metadata lie in the last few megabytes. Ignoring source distributions is bad enough, we can't afford making an optimization that doesn't work for extension modules, which are still an integral part of the Python ecosystem )-:</p> <p>But hey, the ZIP's directory record is warrantied to be at the end of the file! Couldn't we do something about that? The short answer is yes. The long answer is, well, yessssssss! That, plus magic provided by most operating systems, this is what we figured out:</p> <ol> <li>We can download a realatively small chunk at the end of the wheel until it is recognizable as a valid ZIP file.</li> <li>In order for the end of the archive to actually appear as the end to <code>zipfile</code>, we feed to it an object with <code>seek</code> and <code>read</code> defined. As navigating to the rear of the file is performed by calling <code>seek</code> with relative offset and <code>whence=SEEK_END</code> (see <code>man 3 fseek</code> for more details), we are completely able to make the wheels in the cloud to behave as if it were available locally.</li> <img alt="A wheels in the cloud" src="https://gist.githubusercontent.com/McSinyx/4624ab7af8b36d45bc3bf5612e6a6504/raw/230a8caa8deadf92333615961d8b1bd22e5c1c5d/cloud.gif" style="width: 100%;"> <li>For large wheels, it is better to store them in hard disks instead of memory. For smaller ones, it is also preferable to store it as a file to avoid (error-prony and often not really efficient) manual tracking and joining of downloaded segments. We only use a small potion of the wheel, however just in case one is wonderring, we have very little control over when <code>tempfile.SpooledTemporaryFile</code> rolls over, so the memory-disk hybrid is not exactly working as expected.</li> <li>With all these in mind, all we have to do is to define an intermediate object check for local availability and download if needed on calls to <code>read</code>, to lazily provide the data over HTTP and reduce execution time.</li> </ol> <p>The only theoretical challenge left is to keep track of downloaded intervals, which I finally figured out after a few trials and errors. The code was submitted as a pull request to <code>pip</code> at <a href="https://github.com/pypa/pip/pull/8467">GH-8467</a>. A more modern (read: Python 3-only) variant was packaged and uploaded to PyPI under the name of <a href="https://pypi.org/project/lazip/">lazip</a>. I am unaware of any use case for it outside of <code>pip</code>, but it's certainly fun to play with d-:</p> <h2>What's next?</h2> <p>I have been falling short of getting the PRs mention above merged for quite a while. With <code>pip</code>'s next beta coming really soon, I have to somehow make the patches reach a certain standard and enough attention to be part of the pre-release—beta-testing would greatly help the success of the GSoC project. To other GSoC students and mentors reading this, I also hope your projects to turn out successful!</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 22 Jun 2020 19:35:31 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/the-wonderful-wizard-of-o-zip/Second Check-Inhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/second-check-in/<p>Hi everyone and may the odds ever in your favor, especially during this tough time!</p> <h2>What did I do last week?</h2> <p>Not as much I wished, apparently (-:</p> <ul> <li>Finalizing <a href="https://github.com/pypa/pip/pull/8411">the refactoring patch of <code>operations.prepare.prepare_linked_requirement</code></a>.</li> <li><a href="https://github.com/pypa/pip/pull/8423">Nitpicking some logging calls</a>. This (as well as the next one) was to fill up the time my brain not being as productive as I want it to XD</li> <li>Beginning <a href="https://github.com/pypa/pip/pull/8423">to migrate from <code>%</code>- to <code>{}</code>-style logging</a>. The amount of tests failing due to this was way beyond my imagination, but I got functional tests for <code>pip install</code> and unit tests passing now!</li> <li><a href="https://github.com/pypa/pip/pull/8442">Mocking up a working partial wheel download during dependency resolution</a> for <a href="http://www.ei8fdb.org/thoughts/2020/05/test-pips-alpha-resolver-and-help-us-document-dependency-conflicts/">the new resolver</a>.</li> </ul> <h2>Did I get stuck anywhere?</h2> <p>Yes, of course! <a href="https://github.com/pypa/pip/pull/8320">parallel <code>map</code>s</a> are still stalling as well as other small PRs listed above. The failure related to <code>logging</code> are still making me pulling my hair out and the proof of concept for partial wheel downloading is too ugly even for a PoC. I imagine that I will have a lot of clean up to do this week (yay!).</p> <h2>What is coming up next?</h2> <p>I'm trying get the multi-{threading,processing} facilities merged ASAP to start rolling it out in practice. The first thing popping out of my head is to get back <a href="https://github.com/pypa/pip/pull/7962">the multi-threaded <code>pip list -o</code></a>.</p> <p>The other experimental improvement (this phrase does not sound right!) I would like to get done is the partial wheel download. It would be really nice if I can get both of these included as <code>unstable-feature</code>s in <a href="https://github.com/pypa/pip/issues/7628#issuecomment-636319539">the upcoming beta release of pip 20.2</a>.</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 15 Jun 2020 17:47:43 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/second-check-in/Unexpected Things When You're Expectinghttps://blogs.python-gsoc.org/en/mcsinyxs-blog/unexpected-things-when-you-re-expecting/<p>Hi everyone, I hope that you are all doing well and wishes you all good health! The last week has not been really kind to me with a decent amount of academic pressure (my school year is lasting until early Jully). It would be bold to say that I have spent 10 hours working on my GSoC project since the last check-in, let alone the 30 hours per week requirement. That being said, there were still some discoveries that I wish to share.</p> <h2>The <code>multiprocessing[.dummy]</code> wrapper</h2> <p>Most of the time I spent was to finalize the multi{processing,threading} wrapper for <code>map</code> function that submit tasks to the worker pool. To my surprise, it is rather difficult to write something that is not only portable but also easy to read and test.</p> <p>By <a href="https://github.com/pypa/pip/pull/8320/commits/1b4dac53d52c63bf7700eeaea660521a17cce958">the latest commit</a>, I realized the following:</p> <ol> <li>The <code>multiprocessing</code> module was not designed for the implementation details to be abstracted a way entirely. For example, the lazy <code>map</code>s could be really slow without specifying suitable chunk size (to cut the input iterable and distribute them to workers in the pool). By <em>suitable</em>, I mean only an order smaller than the input. This defeats half of the purpose of making it lazy: allowing the input to be evaluated lazily. Luckily, in the use case I'm aiming for, the length of the iterable argument is small and the laziness is only needed for the output (to pipeline download and installation).</li> <li>Mocking <code>import</code> for testing purposes can never be pretty. One reason is that we (Python users) have very little control over the calls of <code>import</code> statements and its lower-level implementation <code>__import__</code>. In order to properly patch this built-in function, unlike for others of the same group, we have to <code>monkeypatch</code> the name from <code>builtins</code> (or <code>__builtins__</code> under Python 2) instead of the module that import stuff. Furthermore, because of the special namespacing, to avoid infinite recursion we need to alias the function to a different name for fallback.</li> <li>To add to the problem, <code>multiprocessing</code> lazily imports the fragile module during pools creation. Since the failure is platform-specific (the lack of <code>sem_open</code>), it was decided to check upon the import of the <code>pip</code>'s module. Although the behavior is easier to reason in human language, testing it requires invalidating cached import and re-import the wrapper module.</li> <li>Last but not least, I now understand the pain of keeping Python 2 compatibility that many package maintainers still need to deal with everyday (although Python 2 has reached its end-of-life, <code>pip</code>, for example, <a href="https://github.com/pypa/pip/issues/6148">will still support it for another year</a>).</li> </ol> <h2>The change in direction</h2> <p>Since last week, my mentor Pradyun Gedam and I set up weekly real-time meeting (a fancy term for video/audio chat in the worldwide quarantine era) for the entire GSoC period. During the last session, we decided to put parallelization of download during resolution on hold, in favor of a more beneficial goal: <a href="https://github.com/pypa/pip/issues/7819">partially download the wheels during dependency resolution</a>.</p> <p><img alt="Assuming I'll reach the goal eventually" src="https://gist.githubusercontent.com/McSinyx/4624ab7af8b36d45bc3bf5612e6a6504/raw/4b61b20949f48536134f5110a17733e27bebcaaf/20200609.png" style="width: 69%;"></p> <p>As discussed by Danny McClanahan and the maintainers of <code>pip</code>, it is feasible to only download a few kB of a wheel to obtain enough metadata for the resolution of dependency. While this is only applicable to wheels (i.e. prebuilt packages), other packaging format only make up less than 20% of the downloads (at least on PyPI), and the figure is much less for the most popular packages. Therefore, this optimization alone could make <a href="http://www.ei8fdb.org/thoughts/2020/05/test-pips-alpha-resolver-and-help-us-document-dependency-conflicts/">the upcoming backtracking resolver</a>'s performance par with the legacy one.</p> <p>During the last few years, there has been a lot of effort being poured into replacing <code>pip</code>'s current resolver that is unable to resolve conflicts. While its correctness will be ensured by some of the most talented and hard-working developers in the Python packaging community, from the users' point of view, it would be better to have its performance not lagging behind the old one. Aside from the increase in CPU cycles for more rigorous resolution, more I/O, especially networking operations is expected to be performed. This is due to <a href="https://github.com/pypa/pip/issues/7406#issuecomment-583891169">the lack of a standard and efficient way to acquire the metadata</a>. Therefore, unlike most package managers we are familiar with, <code>pip</code> has to fetch (and possibly build) the packages solely for dependency informations.</p> <p>Fortunately, <a href="https://www.python.org/dev/peps/pep-0427/#recommended-archiver-features">PEP 427 recommends package builders to place the metadata at the end of the archive</a>. This allows the resolver to only fetch the last few kB using <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests">HTTP range requests</a> for the relevant information. Simply appending <code>Range: bytes=-8000</code> to the request header in <code>pip._internal.network.download</code> makes the resolution process <em>lightning</em> fast. Of course this breaks the installation but I am confident that it is not difficult to implement this optimization cleanly.</p> <p>One drawback of this optimization is the compatibility. Not every Python package warehouse support range requests, and it is not possible verify the partial wheel. While the first case is unavoidable, for the other, hashes checking is usually used for pinned/locked-version requirements, thus no backtracking is done during dependency resolution.</p> <p>Either way, before installation, the packages selected by the resolver can be downloaded in parallel. This warranties a larger crowd of packages, compared to parallelization during resolution, where the number of downloads can be as low as one during trail of different versions of the same package.</p> <p>Unfortunately, I have not been able to do much other than <a href="https://github.com/pypa/pip/pull/8411">a minor clean up</a>. I am looking forward to accomplishing more this week and seeing what this path will lead us too! At the moment, I am happy that I'm able to meet the blog deadline, at least in UTC!</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 08 Jun 2020 20:56:54 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/unexpected-things-when-you-re-expecting/First Check-inhttps://blogs.python-gsoc.org/en/mcsinyxs-blog/first-check-in/<p>Hi everyone, I am McSinyx, a Vietnamese undergraduate student who loves <a href="https://www.gnu.org/philosophy/free-sw.html">free software</a>. This summer I am working with the maintainers and the contributors of <code>pip</code> to make the package manager <a href="https://github.com/pypa/pip/issues/825">download in parallel</a>.</p> <h2>What did I do during the community bonding period?</h2> <p>Aside from bonding with <code>pip</code>'s maintainers and contributors as well as with my mentors, I was also experimenting on the theoretical and technical obstacles blocking this GSoC project. Pradyun Gedam (a mentor of mine) suggested making <a href="https://gist.github.com/McSinyx/513dbff71174fcc79f1cb600e09881af">a proof of concept</a> to determine if parallel downloading can play nicely with <a href="https://pypi.org/project/resolvelib/">ResolveLib</a>'s abstraction and we are reviewing it together. On the technical side, we <code>pip</code>'s committers are exploring <a href="https://github.com/pypa/pip/issues/8169">available options for parallelization</a> and I made an attempt to <a href="https://github.com/pypa/pip/pull/8320">make use of Python's standard worker pool in a portable way</a>.</p> <h2>Did I get stuck anywhere?</h2> <p>Yes, of course! Neither of the experiments above is finished as of this moment. Though, I am optimistic that the issues will not be real blockers and we will figure that out in the next few days.</p> <h2>What is coming up next?</h2> <p>As planned, this week I am going to refactor the package downloading code <code>pip</code>. The main purpose is to decouple the networking code from the package preparation operation and make sure that it is thread-safe.</p> <p>In addition, I am also continuing mentioned experiments to have a better confidence on the future of this GSoC project.</p> <p>To other GSoC students, mentors and admins reading this, I am wishing you all good health and successful projects this summer!</p>vn.mcsinyx@gmail.com (McSinyx)Mon, 01 Jun 2020 10:32:28 +0000https://blogs.python-gsoc.org/en/mcsinyxs-blog/first-check-in/