<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Sam's world</title><link href="https://samroeca.com/" rel="alternate"></link><link href="https://samroeca.com/feed/atom.xml" rel="self"></link><id>https://samroeca.com/</id><updated>2025-01-23T00:00:00-05:00</updated><subtitle>A stream of thought, whim, and wisdom</subtitle><entry><title>I’m Back!</title><link href="https://samroeca.com/back-2025.html" rel="alternate"></link><published>2025-01-23T00:00:00-05:00</published><updated>2025-01-23T00:00:00-05:00</updated><author><name>Samuel Roeca</name></author><id>tag:samroeca.com,2025-01-23:/back-2025.html</id><summary type="html">&lt;p&gt;I&amp;#8217;ve returned. And my blog still&amp;nbsp;works&lt;/p&gt;</summary><content type="html">&lt;p&gt;I haven’t updated this blog since 2020, but lots of things happened between the end of 2020 and January 2025. I moved across the country from New York and settled down in Vancouver, Washington, at the start of the pandemic. During my time in the Pacific Northwest, besides continuing to expand &lt;span class="caps"&gt;KIP&lt;/span&gt; within Kepler, I made a bunch of friends, did a lot of hiking, played lots of trivia with an ever-rotating group, survived the pandemic, got engaged, and got pretty good at playing ukulele (shout-out to the &lt;a href="https://www.doomsdaybrewing.com/"&gt;Doomsday Brewing&lt;/a&gt; safehouse, which has since become the &lt;a href="https://pourchoices.pub/"&gt;Pour Choices Pub&lt;/a&gt;, where I spent every Tuesday jamming with a great group).&lt;/p&gt;
&lt;p&gt;Near the beginning of 2023, my fiancée and I began discussing whether the Pacific Northwest was for us. Over the next two years, we explored more of Portland and Seattle and ultimately concluded that it was time for us to get closer to immediate family. 2024 was a blur: a cross-country move, selling the house, bidding goodbye to friends, and making new ones.&lt;/p&gt;
&lt;p&gt;Which brings me to this blog. Honestly, it’s been so long since I’ve thought about it that I was curious whether it was still functional. It’s a static site hosted on GitHub Pages (source code &lt;a href="https://github.com/pappasam/pappasam.github.io"&gt;here&lt;/a&gt;), so I figured it probably works. Now that we’re in a more stable place and not constantly exploring, if I’m able to publish this piece, I plan to publish more pieces in line with my interests. Through writing my thoughts down in a regular cadence, I hope I can both connect with folks who are interested in my thoughts and identify exciting new areas of exploration through long-form writing.&lt;/p&gt;
&lt;p&gt;But that’s all for now! Gotta go play some pickleball.&lt;/p&gt;</content><category term="Announcements"></category><category term="life-updates"></category></entry><entry><title>How to write a coc.nvim extension</title><link href="https://samroeca.com/coc-plugin.html" rel="alternate"></link><published>2020-08-14T00:00:00-05:00</published><updated>2020-08-14T00:00:00-05:00</updated><author><name>Samuel Roeca</name></author><id>tag:samroeca.com,2020-08-14:/coc-plugin.html</id><summary type="html">&lt;p&gt;Build a &amp;#8220;coc.nvim&amp;#8221; extension that wraps an executable language server. You should learn what makes coc a stand-out &lt;span class="caps"&gt;LSP&lt;/span&gt; client and be able to write your own coc&amp;nbsp;extension.&lt;/p&gt;</summary><content type="html">&lt;div class="toc"&gt;&lt;span class="toctitle"&gt;Table of Contents&lt;/span&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#background"&gt;Background&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#language-server-protocol"&gt;Language Server Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#vscode-client-power"&gt;VSCode: client power&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#cocnvim"&gt;coc.nvim&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#register-a-language-server"&gt;Register a language server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#writing-an-extension"&gt;Writing an extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#deployment"&gt;Deployment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#wrapping-up"&gt;Wrapping up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;In this post, I build a “coc.nvim” extension that wraps an executable language server. By the end of this article, you should both understand what makes coc a stand-out &lt;span class="caps"&gt;LSP&lt;/span&gt; client and be able to write your own coc extension.&lt;/p&gt;
&lt;p&gt;For the interactive parts of this post, you’ll need the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;span class="caps"&gt;POSIX&lt;/span&gt;-compliant terminal (bash, zsh, etc), or the ability to translate&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.js&amp;gt;=8.10.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/yarnpkg/yarn"&gt;yarn&amp;gt;=1.22.4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vim/vim"&gt;Vim 8+&lt;/a&gt; or &lt;a href="https://github.com/neovim/neovim"&gt;Neovim 0.4.4+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/neoclide/coc.nvim"&gt;coc.nvim==0.0.78&lt;/a&gt; (might work on newer versions, but no promises)&lt;/li&gt;
&lt;li&gt;Some knowledge of TypeScript might be helpful&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please disable Python-specific coc extensions (&lt;a href="https://github.com/pappasam/coc-jedi"&gt;coc-jedi&lt;/a&gt;, etc). Some terminology used throughout the post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vim: Vim or Neovim&lt;/li&gt;
&lt;li&gt;vimrc: &lt;code&gt;~/.config/nvim/init.vim&lt;/code&gt; for Neovim or &lt;code&gt;~/.vimrc&lt;/code&gt; for Vim&lt;/li&gt;
&lt;li&gt;coc-settings.json: &lt;code&gt;~/.config/nvim/.coc-settings.json&lt;/code&gt; by default&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="background"&gt;Background&lt;a class="headerlink" href="#background" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/neoclide/coc.nvim"&gt;coc.nvim&lt;/a&gt;, short for “conquer of completion”, is an &lt;a href="https://microsoft.github.io/language-server-protocol/overviews/lsp/overview/"&gt;lsp&lt;/a&gt; client that targets &lt;a href="https://www.vim.org/"&gt;Vim&lt;/a&gt;. Vim (or &lt;a href="https://neovim.io"&gt;NeoVim&lt;/a&gt;) is my favorite text editor / &lt;span class="caps"&gt;IDE&lt;/span&gt;. I like its extensibility, flexibility, and in-terminal slickness. Vim has tools and plugins that make it easy to program in different programming languages. Here are some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript intellisense: &lt;a href="https://github.com/ternjs/tern_for_vim"&gt;tern_for_vim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ruby on rails intellisense: &lt;a href="https://github.com/tpope/vim-rails"&gt;vim-rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;General linting: &lt;a href="https://github.com/vim-syntastic/syntastic"&gt;syntastic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python intellisense: &lt;a href="https://github.com/davidhalter/jedi-vim"&gt;jedi-vim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vim’s diverse plugins and community-supported tooling are what keep me coming back, but not all developers are like me. Visual Studio Code, Nodepad++, and others were more popular than Vim in the &lt;a href="https://insights.stackoverflow.com/survey/2019#technology-_-most-popular-development-environments"&gt;2019 StackOverflow developer survey&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="stack overflow survey" src="./images/coc-plugin/so-2019-dev-env.png"/&gt;&lt;/p&gt;
&lt;p&gt;These popularity differences are starker across programming language communities. For example, Vim’s Java development environment was historically abysmal enough to generate some &lt;a href="https://news.ycombinator.com/item?id=9713478"&gt;heated conversations&lt;/a&gt;. My non-scientific evaluation of the community’s sentiment: obstinate, impractical developers used Vim for Java development while practical, no-nonsense creators preferred Eclipse. Because of this sentiment, tooling for Java evolved in the Eclipse world while Vim’s Java support remained neglected.&lt;/p&gt;
&lt;p&gt;The Java pattern mentioned in the previous paragraph happens, to some extent, in all programming languages. Developers became drawn to editors best-supported their language of choice: PyCharm/Vim for Python, &lt;a href="https://www.eclipse.org/eclipseide/"&gt;Eclipse&lt;/a&gt; for Java, &lt;a href="https://code.visualstudio.com/"&gt;VSCode&lt;/a&gt; for TypeScipt/JavaScript, etc. This, in turn, forced developers into mind-bending context switches when changing languages. To become proficient with a new programming language, a developer needed to learn a whole new text editor and tooling ecosystem! Like asking a Jedi to use another Jedi’s light saber, forcing a developer to learn and use &lt;a href="https://en.wikipedia.org/wiki/Emacs"&gt;Emacs&lt;/a&gt; because it has better support for &lt;code&gt;&amp;lt;insert language here&amp;gt;&lt;/code&gt; can evoke uncomfortable feelings (at best):&lt;/p&gt;
&lt;p&gt;&lt;img alt="Vi versus emacs cartoon" src="https://itsfoss.com/wp-content/uploads/2016/07/vi-emacs.png"/&gt;&lt;/p&gt;
&lt;p&gt;The rise of &lt;a href="https://www.w3schools.com/whatis/whatis_fullstack.asp"&gt;full stack development&lt;/a&gt; and our general expectation that developers be comfortable writing code in more than one language made it so that by mid 2015, we seemed all-but doomed to a life of eternal context switching.&lt;/p&gt;
&lt;p&gt;&lt;img alt="broken bridge" src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Bridging_the_gap_%2825429796194%29.jpg/640px-Bridging_the_gap_%2825429796194%29.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;When society was on the brink, the developers behind VSCode had a big idea: if we separate the tools that understand languages from the tools that edit text, and then give these separate tools a &lt;a href="https://en.wikipedia.org/wiki/Communication_protocol"&gt;communication protocol&lt;/a&gt;, this could enable Vim users to use the same language tools that power Eclipse and VSCode (and vise versa). They called this communication mechanism the “language server protocol”.&lt;/p&gt;
&lt;p&gt;&lt;img alt="fixed bridge" src="https://s0.geograph.org.uk/geophotos/04/26/78/4267872_c1052b8a.jpg"/&gt;&lt;/p&gt;
&lt;h3 id="language-server-protocol"&gt;Language Server Protocol&lt;a class="headerlink" href="#language-server-protocol" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The “Language Server Protocol” (&lt;span class="caps"&gt;LSP&lt;/span&gt;) is a specification that describes communication between a “language client” and a “language server”. The following definitions come from the &lt;a href="https://microsoft.github.io/language-server-protocol/overviews/lsp/overview/"&gt;lsp spec overview&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Language server:&lt;/strong&gt; a program that contains language-specific “smarts” that can communicate with development tooling through the &lt;span class="caps"&gt;LSP&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Language client:&lt;/strong&gt; a program, developed in the environment of a “development tool”, that can send, receive, and act on &lt;span class="caps"&gt;LSP&lt;/span&gt; messages with a “language server”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Development tool:&lt;/strong&gt; Vim, Neovim, &lt;span class="caps"&gt;VS&lt;/span&gt; Code, Eclipse, etc. Basically, a text editor or &lt;span class="caps"&gt;IDE&lt;/span&gt; that software developers use to develop software&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Language Server Protocol" src="https://microsoft.github.io/language-server-protocol/overviews/lsp/img/language-server-sequence.png"/&gt;&lt;/p&gt;
&lt;p&gt;Above, the language client is sitting in the development tool and sending &lt;a href="https://en.wikipedia.org/wiki/JSON-RPC"&gt;&lt;span class="caps"&gt;JSON&lt;/span&gt;-&lt;span class="caps"&gt;RPC&lt;/span&gt;&lt;/a&gt; requests to a language server. All this basically means that your text editor is asking questions to an entity that knows way more about your programming language and responding to its suggested actions. These actions may range from “you should autocomplete some text” to “find this symbol’s definition by opening this file and navigating to this precise position. In short, the orchestrated communication outlined above realizes the following dream:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Language tooling developers write 1 program (the language server) to target every development tool (Vim, &lt;span class="caps"&gt;VS&lt;/span&gt; Code, etc)&lt;/li&gt;
&lt;li&gt;Development tool developers write 1 program (the language client) that knows how to communicate with all language servers&lt;/li&gt;
&lt;li&gt;Software developers have first-class language support for all languages in their favorite editor; no more text-editor context switching!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The above dream seemed like it would herald a new dawn for developers: we can use our favorite tools and have similar, powerful programming language support! As the auguries looked good and a golden age seemed imminent, VSCode’s popularity began generating some bad, familiar omens…&lt;/p&gt;
&lt;p&gt;&lt;img alt="black cat bad omen" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Black_pussy_-_panoramio.jpg/640px-Black_pussy_-_panoramio.jpg"/&gt;&lt;/p&gt;
&lt;h3 id="vscode-client-power"&gt;VSCode: client power&lt;a class="headerlink" href="#vscode-client-power" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;span class="caps"&gt;LSP&lt;/span&gt; dream described in the previous section was similar to &lt;a href="https://en.wikipedia.org/wiki/Tim_Berners-Lee"&gt;Tim Berners-Lee&lt;/a&gt;‘s dream for a world wide web: web clients, supporting web protocols, could enable communication between web servers and a user environment (the operating system). This dream sparked development of all sorts of web clients (browsers) and culminated in the &lt;a href="https://en.wikipedia.org/wiki/Browser_wars"&gt;first browser war&lt;/a&gt;. According to Wikipedia, during this time, Netscape Navigator and Internet Explorer implemented proprietary html tags like &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; for Navigator and &lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt; for Internet Explorer. They also rapidly developed their own features and began diverging from &lt;a href="https://en.wikipedia.org/wiki/World_Wide_Web_Consortium"&gt;&lt;span class="caps"&gt;W3C&lt;/span&gt;&lt;/a&gt;‘s recommended spec. This resulted in efficient Internet Explorer &lt;span class="caps"&gt;HTML&lt;/span&gt; being less efficient or broken on Navigator (and vise versa). This forced users to pick browsers based on custom compatibility with their favorite sites and ultimately resulted in &lt;a href="https://en.wikipedia.org/wiki/United_States_v._Microsoft_Corp."&gt;United States v. Microsoft Corp&lt;/a&gt;. Basically, things got ugly.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Browser wars" src="https://joaopsilva.github.io/talks/End-to-End-JavaScript-with-the-MEAN-Stack/img/ie-vs-netscape.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;Although what is happening today in the text editing community is tamer, it resembles the first browser war in that VSCode has become the de-facto standard for Language Clients. Authors of Language Servers may target VSCode without fully supporting the current &lt;span class="caps"&gt;LSP&lt;/span&gt; spec or by inadvertently supporting VSCode features that are not in the spec. This means that &lt;a href="https://atom.io/"&gt;Atom&lt;/a&gt; users might be using a correctly-implemented language client that does not work with certain language servers. Additionally, there may be no way to easily configure certain language servers without VSCode’s configuration options. This means that if you want to get the best language support possible across all language servers, as of August 2020, you pretty much need to use something that resembles VSCode.&lt;/p&gt;
&lt;p&gt;&lt;img alt="you can't sit with us" src="https://memegenerator.net/img/instances/58875816/you-cant-sit-with-us.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;As the VSCode ecosystem seemed like it had formed an exclusive clique, &lt;a href="https://github.com/chemzqm"&gt;Qiming zhao&lt;/a&gt; (@chemzqm on social media) realized that Vim could join the party by &lt;a href="https://news.ycombinator.com/item?id=19532429"&gt;implementing a VSCode bridge for Neovim&lt;/a&gt;. Qiming zhao named this &lt;span class="caps"&gt;LSP&lt;/span&gt; bridge “coc.nvim”.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Python bridge" src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Amsterdam_Python_Bridge_10.jpg/640px-Amsterdam_Python_Bridge_10.jpg"/&gt;&lt;/p&gt;
&lt;h2 id="cocnvim"&gt;coc.nvim&lt;a class="headerlink" href="#cocnvim" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/neoclide/coc.nvim"&gt;coc.nvim&lt;/a&gt; is a language client for Vim that can be configured similarly to VSCode. Vim is configured with &lt;a href="https://en.wikipedia.org/wiki/Vim_%28text_editor%29#Vim_script"&gt;Vim script&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/C_%28programming_language%29"&gt;C&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Lua_%28programming_language%29"&gt;lua&lt;/a&gt; (if using Neovim), and any other language if you’re feeling fancy enough these days. VSCode is configured with &lt;a href="https://en.wikipedia.org/wiki/JSON"&gt;json&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/TypeScript"&gt;TypeScript&lt;/a&gt;. “coc.nvim” makes it so we can configure “coc.nvim”-managed features using &lt;code&gt;json&lt;/code&gt; and &lt;code&gt;typescript&lt;/code&gt; while still being able to use Vim’s configuration for everything else. Basically, it’s this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="have your cake and eat it too" src="https://teflgeek.files.wordpress.com/2012/05/cake-and-eating-it-too.jpeg"/&gt;&lt;/p&gt;
&lt;h3 id="register-a-language-server"&gt;Register a language server&lt;a class="headerlink" href="#register-a-language-server" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Without an extension, coc behaves like a simple language client. For simplicity’s sake, let’s assume we’re all Python developers and that we’d like to use a language server named &lt;a href="https://github.com/pappasam/jedi-language-server"&gt;jedi-language-server&lt;/a&gt; with Vim.&lt;/p&gt;
&lt;p&gt;Within your current Python environment, install jedi-language-server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;jedi-language-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run jedi-language-server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jedi-language-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the process hangs; it’s waiting to receive standard input and will respond with standard output. This server can run inside of Vim by placing the following code in the &lt;code&gt;languageserver&lt;/code&gt; section of your coc-settings.json:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"languageserver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jedi-language-server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"filetypes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you’ve configured coc.nvim and installed jedi-language-server in an accessible location, Vim and jedi-language-server will communicate to provide you autocompletion, goto definition, etc. Hooray!&lt;/p&gt;
&lt;p&gt;&lt;img alt="message teamwork" src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/A_message_written_on_rice_paper_is_put_into_a_container_and_attached_to_a_carrier_pigeon_by_members_of_61st_Division_Signals_at_Ballymena%2C_Northern_Ireland%2C_3_July_1941._H11281.jpg/612px-thumbnail.jpg"/&gt;&lt;/p&gt;
&lt;h4 id="manual-configuration"&gt;Manual configuration&lt;a class="headerlink" href="#manual-configuration" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It turns out there are some practical problems with expecting users to configure jedi-language-server manually:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Remembering to manually install a language server can annoy people; shouldn’t the editor remember to do that?&lt;/li&gt;
&lt;li&gt;We’d like to configure the language server; can’t that configuration have pretty autocompletion and look like the configuration I can use with all the other VSCode-like configuration options?&lt;/li&gt;
&lt;li&gt;It’s good &lt;span class="caps"&gt;UX&lt;/span&gt; to prevent preventable problems.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is where coc extensions come into play: they avoid the above problems by providing default configurations and features. Users can seamlessly use a language server with an extension.&lt;/p&gt;
&lt;p&gt;&lt;img alt="extension" src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Europlug_extension_cord.jpg/640px-Europlug_extension_cord.jpg"/&gt;&lt;/p&gt;
&lt;h3 id="writing-an-extension"&gt;Writing an extension&lt;a class="headerlink" href="#writing-an-extension" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We will address 2 requirements in this post:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Prevent the user from needing to write manual configuration for jedi-language-server in coc-settings.json&lt;/li&gt;
&lt;li&gt;Allow the user to configure the server as “disabled” if needed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The reader may address other requirements as a post-blog exercise.&lt;/p&gt;
&lt;h4 id="project-skaffolding"&gt;Project skaffolding&lt;a class="headerlink" href="#project-skaffolding" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In &lt;code&gt;~/src&lt;/code&gt;, use &lt;a href="https://github.com/fannheyward/create-coc-extension"&gt;create-coc-extension&lt;/a&gt; to create a project skeleton:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;yarn&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;coc-extension&lt;span class="w"&gt; &lt;/span&gt;coc-jls
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;? Project title: coc-jls&lt;/span&gt;
&lt;span class="go"&gt;? Project description: A great project&lt;/span&gt;
&lt;span class="go"&gt;? Author full name: Your name&lt;/span&gt;
&lt;span class="go"&gt;? Author email address: your.name@domain.com&lt;/span&gt;
&lt;span class="go"&gt;? Initialize a git repository? Yes&lt;/span&gt;
&lt;span class="go"&gt;? Install node dependencies? Yes&lt;/span&gt;
&lt;span class="go"&gt;  installing node dependencies...&lt;/span&gt;
&lt;span class="go"&gt;Done...&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;coc-jls
&lt;span class="gp"&gt;$ &lt;/span&gt;yarn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should have the following version-controlled files in coc-jls:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;LICENSE
README.md
package.json
src/
  index.ts
  lists.ts
tsconfig.json
webpack.config.js
yarn.lock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Delete the files in &lt;code&gt;src&lt;/code&gt;; we’ll be writing these from scratch.&lt;/p&gt;
&lt;h4 id="first-file"&gt;First file&lt;a class="headerlink" href="#first-file" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To get a simple wrapper around an existing jedi-language-server executable, place the following code in &lt;code&gt;src/index.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExtensionContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LanguageClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'coc.nvim'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ExtensionContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;serverOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'jedi-language-server'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// run jls&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clientOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;documentSelector&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'python'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// run jls on py files&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LanguageClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;'coc-jls'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// the id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;'coc-jls'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// the name of the language server&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;serverOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;clientOptions&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registLanguageClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This code tells coc to register a new language server named &lt;code&gt;coc-jls&lt;/code&gt; that executes the command &lt;code&gt;jedi-language-server&lt;/code&gt; when Vim edits Python file(s). Now build the project with the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;yarn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Open an empty file (test.py works) with Vim and run the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;runtimepath&lt;/span&gt;^&lt;span class="p"&gt;=~&lt;/span&gt;&lt;span class="sr"&gt;/src/&lt;/span&gt;&lt;span class="k"&gt;sandbox&lt;/span&gt;/coc&lt;span class="p"&gt;-&lt;/span&gt;jls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This command will help coc discover your extension. Depending on your coc configuration, you should see diagnostics, have autocompletion, etc. In the long term, you may want to manage your coc extension as a plugin or a Vim &lt;a href="https://shapeshed.com/vim-packages"&gt;package&lt;/a&gt;, but we’ll stay local and simple for now.&lt;/p&gt;
&lt;h4 id="user-configuration"&gt;User configuration&lt;a class="headerlink" href="#user-configuration" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We’ve wrapped jedi-language-server with a coc extension and now it’s time to add some user configuration! Coc provides a simple mechanism to help users communicate their configuration values like VSCode: through the package.json. Make sure the following lines are in your package.json:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"contributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"configuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"coc-jls configuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"coc-jls.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enable coc-jls extension"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This says that the coc-jls extension contributes a configuration key &lt;code&gt;coc-jls.enabled&lt;/code&gt; whose default value is &lt;code&gt;true&lt;/code&gt;. If a user wants to disable &lt;code&gt;coc-jls&lt;/code&gt;, they must add the following line to the top level of coc-settings.json:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"coc-jls.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we must add some logic to our typescript code to read the user configuration and disable the server if the user has chosen not to have it enabled:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExtensionContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LanguageClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'coc.nvim'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ExtensionContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// BEGIN NEW CODE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'coc-jls'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;isEnable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'enable'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isEnable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// END NEW CODE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;serverOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'jedi-language-server'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// run jls&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clientOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;documentSelector&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'python'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// run jls on py files&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LanguageClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;'coc-jls'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// the id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;'coc-jls'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// the name of the language server&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;serverOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;clientOptions&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registLanguageClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run &lt;code&gt;yarn&lt;/code&gt; and voilà: users can now disable coc-jls without needing to uninstall the extension. Success!&lt;/p&gt;
&lt;h3 id="deployment"&gt;Deployment&lt;a class="headerlink" href="#deployment" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To “deploy” this extension, one approach is to upload your project GitHub, add it to your Package or Plugin manager configuration, and install. If you’re using &lt;a href="https://github.com/junegunn/vim-plug"&gt;Vim-Plug&lt;/a&gt;, put the following in your vimrc:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Plug &lt;span class="s1"&gt;'your-username/coc-jls'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; {&lt;span class="s1"&gt;'do'&lt;/span&gt;: &lt;span class="s1"&gt;'yarn install --frozen-lockfile &amp;amp;&amp;amp; yarn build'&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;PlugInstall
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will download your Git repository, install all necessary dependencies, build your project, and make sure it’s automatically added to Vim’s runtime path for coc can discover the extension.&lt;/p&gt;
&lt;p&gt;You can also do all this manually if you know what you’re doing.&lt;/p&gt;
&lt;p&gt;Coc extensions can also be deployed to &lt;a href="https://www.npmjs.com"&gt;npm&lt;/a&gt;. This method is out of scope for this post, but you should be able to figure it out by referencing the &lt;a href="https://github.com/pappasam/coc-jedi"&gt;coc-jedi&lt;/a&gt; codebase.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;a class="headerlink" href="#wrapping-up" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this point, you should have a working coc extension that wraps an executable language server. You may be wondering how you can add more features, automatically download and manage the executable for your users, and make your coc extension the most user friendly interface since the &lt;a href="https://en.wikipedia.org/wiki/IPod_click_wheel"&gt;iPod click wheel&lt;/a&gt;. Worry not: these features are implemented in &lt;a href="https://github.com/pappasam/coc-jedi"&gt;coc-jedi&lt;/a&gt;. Please refer to that codebase and ask any further questions in the comments below. And don’t feel obligated to restrict your wanderings to jedi-language-server: you can wrap &lt;strong&gt;any&lt;/strong&gt; executable language server in a coc extension! Please use your powers for good.&lt;/p&gt;
&lt;p&gt;&lt;img alt="jedi programmer" src="https://www.kumulos.com/wp-content/uploads/2012/09/Computer-programming-jedi.jpg"/&gt;&lt;/p&gt;</content><category term="Software Development"></category><category term="vim"></category><category term="neovim"></category><category term="lsp"></category></entry><entry><title>Gitignore: two tricks</title><link href="https://samroeca.com/ignore-git.html" rel="alternate"></link><published>2018-12-02T00:00:00-05:00</published><updated>2018-12-02T00:00:00-05:00</updated><author><name>Samuel Roeca</name></author><id>tag:samroeca.com,2018-12-02:/ignore-git.html</id><summary type="html">&lt;p&gt;Discover two techniques to ignore files from a Git-based version controlled project. The first technique enables Git to ignore every file in a directory other than the directory itself. The second technique enables a user to automatically ignore a file without adding it to a project&amp;#8217;s root&amp;nbsp;gitignore.&lt;/p&gt;</summary><content type="html">&lt;div class="toc"&gt;&lt;span class="toctitle"&gt;Table of Contents&lt;/span&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#ignore-directory-contents-keep-directory"&gt;Ignore directory contents, keep directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#ignore-my-weird-files-in-secret"&gt;Ignore my weird files (in secret)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;Most modern software developers are aware of &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt; and its accompanying &lt;a href="https://git-scm.com/docs/gitignore"&gt;gitignore&lt;/a&gt; file. This post offers two lesser-known-yet-useful techniques to ignore files from a Git-based version controlled project. The first technique enables Git to ignore every file in a directory &lt;strong&gt;but&lt;/strong&gt; the directory itself. The second technique enables a user to automatically ignore a file without adding it to a project’s root gitignore.&lt;/p&gt;
&lt;p&gt;Note: when discussing “&lt;a href="https://git-scm.com/docs/gitignore"&gt;gitignore&lt;/a&gt;” and “&lt;a href="http://ternjs.net/doc/manual.html#configuration"&gt;tern-project&lt;/a&gt;“, I may sometimes add a “.” before the filename. Whether I &lt;a href="http://www.yodaquotes.net/try-not-do-or-do-not-there-is-no-try/"&gt;do or do not&lt;/a&gt;, I’m talking about the same file (eg, “.gitignore” == “gitignore”).&lt;/p&gt;
&lt;h2 id="ignore-directory-contents-keep-directory"&gt;Ignore directory contents, keep directory&lt;a class="headerlink" href="#ignore-directory-contents-keep-directory" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is often convenient to have a directory whose contents are not committed to version control. For example, this directory may contain large non-general data files (images, financial data, etc). Additionally, these could be small files with sensitive information specific to one person (passwords, ssh keys, etc). Either way, we do not want these files committed to our project’s version control system. For the purposes of this conversation, we’ll call this folder “instance” and place it at the root of our project.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;├── app
│   └── __init__.py
├── .gitignore          &amp;lt;-- Project gitignore
└── instance
    ├── big-file.jpg
    └── passwords.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A naive approach suggests that we add the instance folder to our project’s gitignore:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;*.pyc
__pycache__/
# other ignored lines
instance/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unfortunately, this proves problematic. What happens if our system relies on a file being in the instance folder at runtime? A user of our system would need to know to create the instance folder &lt;span class="caps"&gt;AND&lt;/span&gt; place a file there, forcing them to perform two manual steps.&lt;/p&gt;
&lt;p&gt;A preferable solution: place a gitignore in the “instance” folder with special contents.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;├── app
│   └── __init__.py
├── .gitignore          &amp;lt;-- Project gitignore
└── instance
    ├── big-file.jpg
    ├── .gitignore      &amp;lt;-- Special gitignore
    └── passwords.txt
-----------------------------------
special .gitignore contents:
# Ignore all files in this directory
# EXCEPT for this .gitignore file
*
!.gitignore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I find myself using this trick so often that I’ve added the instance folder and its accompanying gitignore file into my project skeletons.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/.bashrc (I actually use zsh + ~/.zshrc, but it's the same)&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make_instance&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;instance
&lt;span class="w"&gt;  &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;instance/.gitignore&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;EOL&lt;/span&gt;
&lt;span class="s"&gt;# Ignore all files in this directory&lt;/span&gt;
&lt;span class="s"&gt;# EXCEPT for this .gitignore file&lt;/span&gt;
&lt;span class="s"&gt;*&lt;/span&gt;
&lt;span class="s"&gt;!.gitignore&lt;/span&gt;
&lt;span class="s"&gt;EOL&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, you may note that this technique does commit one file (the &lt;a href="https://git-scm.com/docs/gitignore"&gt;gitignore&lt;/a&gt;) to version control. &lt;span class="caps"&gt;IMHO&lt;/span&gt;, this is a small price to pay for elegance.&lt;/p&gt;
&lt;h2 id="ignore-my-weird-files-in-secret"&gt;Ignore my weird files (in secret)&lt;a class="headerlink" href="#ignore-my-weird-files-in-secret" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My &lt;a href="https://github.com/pappasam/dotfiles"&gt;development environment&lt;/a&gt; is highly customized. I’m an advanced user of &lt;a href="https://neovim.io/charter/"&gt;Neovim&lt;/a&gt;, &lt;a href="https://www.ocf.berkeley.edu/~ckuehl/tmux/"&gt;Tmux&lt;/a&gt;, &lt;a href="http://zsh.sourceforge.net/Intro/intro_1.html#SEC1"&gt;Zsh&lt;/a&gt;, and other &lt;a href="https://en.wikipedia.org/wiki/Unix-like"&gt;Unix-like&lt;/a&gt; utilities, and have set up a cross-language, bespoke &lt;span class="caps"&gt;IDE&lt;/span&gt; with a configuration unto itself. Benefits include software development bliss. One downside: my environment may require files that I should not commit to another person’s repository.&lt;/p&gt;
&lt;p&gt;One example: when programming in Javascript, I rely on a &lt;a href="https://github.com/ternjs/tern"&gt;Tern&lt;/a&gt; server for auto-completion in &lt;a href="https://neovim.io/charter/"&gt;Neovim&lt;/a&gt;. &lt;a href="https://github.com/ternjs/tern"&gt;Tern&lt;/a&gt; is configurable with a &lt;a href="http://ternjs.net/doc/manual.html#configuration"&gt;tern-project&lt;/a&gt; file which lets &lt;a href="https://github.com/ternjs/tern"&gt;Tern&lt;/a&gt; know important things like the project’s &lt;a href="https://en.wikipedia.org/wiki/ECMAScript"&gt;ECMAScript&lt;/a&gt; version, which &lt;a href="http://voidcanvas.com/node-vs-browsers/"&gt;runtime environment&lt;/a&gt; it’s expecting, etc. These configurations differ by project, so it’s often necessary for me to place a customized &lt;a href="http://ternjs.net/doc/manual.html#configuration"&gt;tern-project&lt;/a&gt; file at the root of a Javascript project.&lt;/p&gt;
&lt;p&gt;If I own a Javascript codebase and control its development, placing a &lt;a href="http://ternjs.net/doc/manual.html#configuration"&gt;tern-project&lt;/a&gt; file at my project’s root is a no-brainer. Not only can collaborators help me keep it up to date, but I can also influence more people to adopt &lt;a href="https://github.com/ternjs/tern"&gt;Tern&lt;/a&gt;! Unfortunately, if I don’t own the codebase I’m working on, things become a bit trickier. I want to benefit from &lt;a href="https://github.com/ternjs/tern"&gt;Tern&lt;/a&gt; but I don’t want my first contributions on a project to include my custom &lt;a href="https://github.com/pappasam/dotfiles"&gt;development environment&lt;/a&gt; configuration. I’d simply add this file to the project’s .gitignore file, but that would still be an me-specific contribution, so I’m left with two choices. Either I manually ignore &lt;a href="http://ternjs.net/doc/manual.html#configuration"&gt;tern-project&lt;/a&gt; every time I “git add/commit”, or I find a way to make Git do that work for me.&lt;/p&gt;
&lt;p&gt;Luckily, Git has a built-in way for users to ignore their own weird files without needing to make any updates to a project’s version-controlled files. This method is mentioned in &lt;a href="https://git-scm.com/docs/gitignore"&gt;gitignore&lt;/a&gt;‘s documentation, but not many people are aware it exists.&lt;/p&gt;
&lt;p&gt;Using your favorite editor, go to the root of any &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt; repository, and open the text file “.git/info/exclude”. It should look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Contents of PROJECT-ROOT/.git/info/exclude:&lt;/span&gt;
&lt;span class="c1"&gt;# git ls-files --others --exclude-from=.git/info/exclude&lt;/span&gt;
&lt;span class="c1"&gt;# Lines that start with '#' are comments.&lt;/span&gt;
&lt;span class="c1"&gt;# For a project mostly in C, the following would be a good set of&lt;/span&gt;
&lt;span class="c1"&gt;# exclude patterns (uncomment them if you want to use them):&lt;/span&gt;
&lt;span class="c1"&gt;# *.[oa]&lt;/span&gt;
&lt;span class="c1"&gt;# *~&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Simply add your desired file patterns here and they will be ignored by you, and only you.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Contents of PROJECT-ROOT/.git/info/exclude:&lt;/span&gt;
&lt;span class="c1"&gt;# git ls-files --others --exclude-from=.git/info/exclude&lt;/span&gt;
&lt;span class="c1"&gt;# Lines that start with '#' are comments.&lt;/span&gt;
&lt;span class="c1"&gt;# For a project mostly in C, the following would be a good set of&lt;/span&gt;
&lt;span class="c1"&gt;# exclude patterns (uncomment them if you want to use them):&lt;/span&gt;
&lt;span class="c1"&gt;# *.[oa]&lt;/span&gt;
&lt;span class="c1"&gt;# *~&lt;/span&gt;
.tern-project
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you’ve done this, I recommend submitting one or two impressive pull requests. You should be invited as a repository &lt;a href="https://help.github.com/articles/github-glossary/#collaborator"&gt;collaborator&lt;/a&gt; (or its equivalent) in no time. With credentials in hand, you can then suggest the group adopt your awesome &lt;a href="https://github.com/ternjs/tern"&gt;Tern&lt;/a&gt;-based workflow (and your humble &lt;a href="http://ternjs.net/doc/manual.html#configuration"&gt;tern-project&lt;/a&gt; file) into the project. At this point, you should remove the “.tern-project” line from .git/info/exclude.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="headerlink" href="#conclusion" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ignoring directory contents while retaining directories is easy. Privately ignoring the files only you care about (for now) is also easy. Hopefully you learned something from this post and happy hacking!&lt;/p&gt;</content><category term="Software Development"></category><category term="vcs"></category><category term="git"></category></entry><entry><title>Python function pipelines</title><link href="https://samroeca.com/python-function-pipelines.html" rel="alternate"></link><published>2018-06-02T00:00:00-05:00</published><updated>2018-06-02T00:00:00-05:00</updated><author><name>Samuel Roeca</name></author><id>tag:samroeca.com,2018-06-02:/python-function-pipelines.html</id><summary type="html">&lt;p&gt;Develop Unix-like pipelines in&amp;nbsp;Python.&lt;/p&gt;</summary><content type="html">&lt;div class="toc"&gt;&lt;span class="toctitle"&gt;Table of Contents&lt;/span&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-problem"&gt;The problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#unix-pipes"&gt;Unix pipes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#example"&gt;Example&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#solution-1-name-each-step-in-the-pipeline"&gt;Solution 1: name each step in the pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#solution-2-use-the-reduce-function"&gt;Solution 2: use the “reduce” function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#solution-3-custom-pipeline-function"&gt;Solution 3: custom “pipeline” function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#full-script"&gt;Full script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;If your Python code represents a function pipeline, it should look like a function pipeline. This post presents a simple, strongly-typed function pipeline for your personal projects to make beautiful, explicit, Unix-like pipelines in Python.  Requires Python 3.6 or greater.&lt;/p&gt;
&lt;p&gt;I was reading through the pytorch reinforcement learning &lt;a href="https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html"&gt;documentation&lt;/a&gt; today and came across the following irksome pattern:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bn1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conv1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bn2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conv2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bn3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conv3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;..&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The manipulation and reassignment of a variable &lt;em&gt;x&lt;/em&gt; to itself as it is passed through a neural network’s gateways makes neural networks that much harder to follow. &lt;span class="caps"&gt;IMHO&lt;/span&gt;, explicit variable &lt;strong&gt;reassignment&lt;/strong&gt; is almost always bad because it makes it hard for my limited human brain to track a variable name’s associated data during program execution. If a program tells me that &lt;em&gt;x&lt;/em&gt; is an integer on line 10 and then &lt;strong&gt;reassigns&lt;/strong&gt; it to a string on line 20, I will want to delete said program from my computer, take a cold shower, and exact revenge on the program’s author. With that established, let’s just avoid reassignment altogether.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The problem&lt;a class="headerlink" href="#the-problem" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the above example, variable reassignment is convenient. It prevents the programmer from needing to come up with new names for each state in the connected pipeline. How can we find an elegant way of avoiding explicit variable reassignment in the pipeline use-case while still producing readable, performant code? The answer: take inspiration from Unix pipes!&lt;/p&gt;
&lt;h2 id="unix-pipes"&gt;Unix pipes&lt;a class="headerlink" href="#unix-pipes" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the Unix shell, virtually everything we work with has the same type: a &lt;a href="https://en.wikipedia.org/wiki/Everything_is_a_file"&gt;file&lt;/a&gt;. “Everything is a File” makes it easy to chain functions together because all commands take files as inputs and return files as outputs.  One common way to chain system calls in Unix is called the &lt;a href="https://en.wikipedia.org/wiki/Anonymous_pipe"&gt;anonymous pipe&lt;/a&gt;, which enables programmers to chain command line programs together to manipulate a text stream. See the following example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c1"&gt;# Count the number of words&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello hello hello world world"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wc&lt;span class="w"&gt; &lt;/span&gt;-w

&lt;span class="c1"&gt;# Count the number of words that are not "hello"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello hello hello world world"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/hello//g'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wc&lt;span class="w"&gt; &lt;/span&gt;-w

&lt;span class="c1"&gt;# Count the number of words that are not "world"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello hello hello world world"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/world//g'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wc&lt;span class="w"&gt; &lt;/span&gt;-w

&lt;span class="c1"&gt;# Should print the following to console:&lt;/span&gt;
&lt;span class="c1"&gt;# 5&lt;/span&gt;
&lt;span class="c1"&gt;# 2&lt;/span&gt;
&lt;span class="c1"&gt;# 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that data flows &lt;strong&gt;through&lt;/strong&gt; the pipeline and no variable reassignment is used. The Unix pipeline is beautiful in this regard; I’d like to build something similar in Python.&lt;/p&gt;
&lt;h2 id="example"&gt;Example&lt;a class="headerlink" href="#example" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s say we have the following three functions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our &lt;strong&gt;example problem&lt;/strong&gt;: take an initial integer 0 and add 5, then 6, then 7 to it using our three functions so that our final result is 18.&lt;/p&gt;
&lt;h3 id="solution-1-name-each-step-in-the-pipeline"&gt;Solution 1: name each step in the pipeline&lt;a class="headerlink" href="#solution-1-name-each-step-in-the-pipeline" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We can do slightly better than the pytorch example and create a unique variable
name for each step in the process.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;x_0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;x_5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x_6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'x=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Besides being pretty ugly / hard to read, this creates some useless names in our module’s scope. Do we really need to give each step its own name? We don’t use the steps anywhere else and this makes the pipeline pretty hard to edit. This solution almost makes we wish we could go back to reassignment. Fortunately, the entire pipeline can be expressed with one name thanks to the “reduce” function.&lt;/p&gt;
&lt;h3 id="solution-2-use-the-reduce-function"&gt;Solution 2: use the “reduce” function&lt;a class="headerlink" href="#solution-2-use-the-reduce-function" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'y=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This example uses the standard library “reduce” function. Originally, “reduce” was intended to take a list of values and collapse them into one value. This use-case is described well in the &lt;a href="https://docs.python.org/3/library/functools.html#functools.reduce"&gt;Python documentation&lt;/a&gt; and in this &lt;a href="https://www.youtube.com/watch?v=ZrZ6vJGiE8I"&gt;YouTube&lt;/a&gt; video. Here, we use it a bit differently. Instead of taking a list of values and applying a “collapsing” function to them, we take a list of functions and pass a value through them. This has obvious advantages over solution 1: we can easily swap new functions in and out of the pipeline to suit our needs without needing to adjust any other code / rename pipeline steps (because we haven’t named the steps at all!).&lt;/p&gt;
&lt;p&gt;This solution frees us from naming each step in our pipeline but it does have some disadvantages: “reduce” is kind of hard to read and this use-case isn’t quite standard. Whenever we find ourselves using a standard library function in a confusing way, that’s a signal that we should probably define our own function to make this clearer to ourselves and to those who read our code.&lt;/p&gt;
&lt;h3 id="solution-3-custom-pipeline-function"&gt;Solution 3: custom “pipeline” function&lt;a class="headerlink" href="#solution-3-custom-pipeline-function" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;

&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;function_pipeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;'''A generic Unix-like pipeline&lt;/span&gt;

&lt;span class="sd"&gt;    :param value: the value you want to pass through a pipeline&lt;/span&gt;
&lt;span class="sd"&gt;    :param function_pipeline: an ordered list of functions that&lt;/span&gt;
&lt;span class="sd"&gt;        comprise your pipeline&lt;/span&gt;
&lt;span class="sd"&gt;    '''&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;function_pipeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;function_pipeline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'z=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This solution is elegant and explicit. It is generic and works with mypy. As long as our function pipeline contains only functions that take our value’s type and return our value’s type (similar to Unix command line utilities, where everything is a file), this pipeline will successfully pass our value in a type-safe way.&lt;/p&gt;
&lt;h2 id="full-script"&gt;Full script&lt;a class="headerlink" href="#full-script" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="sd"&gt;'''Simple function example'''&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;

&lt;span class="n"&gt;x_0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;x_5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x_6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'x=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'y=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;

&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;function_pipeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;'''A generic Unix-like pipeline&lt;/span&gt;

&lt;span class="sd"&gt;    :param value: the value you want to pass through a pipeline&lt;/span&gt;
&lt;span class="sd"&gt;    :param function_pipeline: an ordered list of functions that&lt;/span&gt;
&lt;span class="sd"&gt;        comprise your pipeline&lt;/span&gt;
&lt;span class="sd"&gt;    '''&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;function_pipeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;function_pipeline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;add_5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;add_7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'z=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="headerlink" href="#conclusion" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If your system resembles a pipeline, don’t reassign your piped variable to itself. There is a better way and it’s pretty much built into Python.  You’ll just need to care enough to use it.&lt;/p&gt;</content><category term="Software Development"></category><category term="python"></category><category term="unix"></category><category term="functional programming"></category></entry><entry><title>Smaller python docker containers</title><link href="https://samroeca.com/docker-python-install-wheels.html" rel="alternate"></link><published>2018-04-21T00:00:00-05:00</published><updated>2018-04-21T00:00:00-05:00</updated><author><name>Samuel Roeca</name></author><id>tag:samroeca.com,2018-04-21:/docker-python-install-wheels.html</id><summary type="html">&lt;p&gt;Create a small Python Docker container using Docker multi-stage builds and Python&amp;nbsp;wheels.&lt;/p&gt;</summary><content type="html">&lt;div class="toc"&gt;&lt;span class="toctitle"&gt;Table of Contents&lt;/span&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-problem"&gt;The problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#examples"&gt;Examples&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#setup"&gt;Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#example-1-broken-build-requiring-a-c-compiler"&gt;Example 1: broken build requiring a C compiler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#example-2-large-build-with-c-compiler-installed"&gt;Example 2: large build with C compiler installed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#example-3-failed-attempt-at-simply-uninstalling-c-compiler"&gt;Example 3: failed attempt at simply “uninstalling” C compiler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#example-4-small-final-build-without-c-compiler"&gt;Example 4: small final build without C compiler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#explanation"&gt;Explanation&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#copying-betwen-docker-build-stages-in-multi-stage-build"&gt;Copying betwen Docker build stages in multi-stage build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#difference-between-pip-install-and-pip-wheel"&gt;Difference between “pip install” and “pip wheel”&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#special-thanks"&gt;Special thanks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;If your Docker Python build requires system dependencies that are &lt;span class="caps"&gt;NOT&lt;/span&gt; required at runtime, structure your build as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/"&gt;multi-stage build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Stage 1 installs system dependencies and uses them to build local &lt;a href="https://pythonwheels.com/"&gt;wheels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Stage 2 begins from the same base as Stage 1, copies wheels from Stage 1, and installs the wheels&lt;/li&gt;
&lt;li&gt;The rest of your build will be based on Stage 2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you follow these steps, you’ll end up with the smallest-possible Python Docker container with all your Python dependencies intact.&lt;/p&gt;
&lt;p&gt;Note: this post references Docker 18.03, Python 3.6, and pip 10. I assume that you are running &lt;a href="https://github.com/python/cpython"&gt;CPython&lt;/a&gt; (Python’s reference implementation).&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The problem&lt;a class="headerlink" href="#the-problem" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We want to do a Python system build using Docker. Python system builds often require installing third-party code. This third-party code may contain code or resources that must be compiled during their installation. For simplicity’s sake, assume we are talking about source code in the C programming language. Since a Docker container will be our “target machine”, we’ll need a C compiler in our Docker container. Unfortunately, C compilers are large programs. Since we plan to scale our number of containers up and down based on the demand for its provided service, the image should ideally be as small as possible.&lt;/p&gt;
&lt;p&gt;Basically, we want to build C code with a C compiler and then throw away
the C compiler to save space in our deployment image.&lt;/p&gt;
&lt;h2 id="examples"&gt;Examples&lt;a class="headerlink" href="#examples" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following examples should clarify the problem and its resolution.
Note: I’m assuming that you’re using a
&lt;a href="https://en.wikipedia.org/wiki/POSIX"&gt;&lt;span class="caps"&gt;POSIX&lt;/span&gt;&lt;/a&gt;-inspired system.&lt;/p&gt;
&lt;h3 id="setup"&gt;Setup&lt;a class="headerlink" href="#setup" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Copy the following Makefile into your current working directory.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;.PHONY: build-break
build-break:
    docker build -t blog-python:break -f ./Dockerfile.break .

.PHONY: build-big
build-big:
    docker build -t blog-python:big -f ./Dockerfile.big .
    docker images

.PHONY: build-uninstall-big
build-uninstall-big:
    docker build -t blog-python:big-uninstall -f ./Dockerfile.uninstall .
    docker images

.PHONY: build-small
build-small:
    docker build -t blog-python:small -f ./Dockerfile.small .
    docker images
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: you might need to convert spaces to tabs.&lt;/p&gt;
&lt;h3 id="example-1-broken-build-requiring-a-c-compiler"&gt;Example 1: broken build requiring a C compiler&lt;a class="headerlink" href="#example-1-broken-build-requiring-a-c-compiler" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We have a simple, &lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint"&gt;entrypoint&lt;/a&gt;-less Docker container in which we must install &lt;a href="https://uwsgi-docs.readthedocs.io/en/latest/"&gt;uWSGI&lt;/a&gt;. In the uWSGI &lt;a href="https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html"&gt;quickstart&lt;/a&gt; guide, its developers clarify that it “is a (big) C application, so you need a C compiler (like gcc or clang) and the Python development headers”.&lt;/p&gt;
&lt;p&gt;Copy the following code into a file called “Dockerfile.break”:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.6-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;breakimage&lt;/span&gt;

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;uwsgi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run the following shell command in the same directory as your Dockerfile.break.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;build-break

&lt;span class="go"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;span class="go"&gt;  File "&amp;lt;string&amp;gt;", line 1, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;  File "/tmp/pip-install-tkd8plx9/uwsgi/setup.py", line 137, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;    'Programming Language :: Python :: 3.6',&lt;/span&gt;
&lt;span class="go"&gt;  File "/usr/local/lib/python3.6/site-packages/setuptools/__init__.py", line 129, in setup&lt;/span&gt;
&lt;span class="go"&gt;    return distutils.core.setup(**attrs)&lt;/span&gt;
&lt;span class="go"&gt;  File "/usr/local/lib/python3.6/distutils/core.py", line 148, in setup&lt;/span&gt;
&lt;span class="go"&gt;    dist.run_commands()&lt;/span&gt;
&lt;span class="go"&gt;  File "/usr/local/lib/python3.6/distutils/dist.py", line 955, in run_commands&lt;/span&gt;
&lt;span class="go"&gt;    self.run_command(cmd)&lt;/span&gt;
&lt;span class="go"&gt;  File "/usr/local/lib/python3.6/distutils/dist.py", line 974, in run_command&lt;/span&gt;
&lt;span class="go"&gt;    cmd_obj.run()&lt;/span&gt;
&lt;span class="go"&gt;  File "/tmp/pip-install-tkd8plx9/uwsgi/setup.py", line 77, in run&lt;/span&gt;
&lt;span class="go"&gt;    conf = uc.uConf(get_profile())&lt;/span&gt;
&lt;span class="go"&gt;  File "/tmp/pip-install-tkd8plx9/uwsgi/uwsgiconfig.py", line 747, in __init__&lt;/span&gt;
&lt;span class="go"&gt;    raise Exception("you need a C compiler to build uWSGI")&lt;/span&gt;
&lt;span class="go"&gt;Exception: you need a C compiler to build uWSGI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At the end of our failed build, we see this Traceback (in addition to other helpful messages). Consistent with the uWSGI documentation, our system has said that we “need a C compiler to build uWSGI”. We’ll do that in example 2.&lt;/p&gt;
&lt;h3 id="example-2-large-build-with-c-compiler-installed"&gt;Example 2: large build with C compiler installed&lt;a class="headerlink" href="#example-2-large-build-with-c-compiler-installed" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this example, we’ll install our system dependencies so uWSGI can actually be built.&lt;/p&gt;
&lt;p&gt;Copy the following code into a file called “Dockerfile.big”:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.6-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;bigimage&lt;/span&gt;

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--no-cache&lt;span class="w"&gt; &lt;/span&gt;linux-headers&lt;span class="w"&gt; &lt;/span&gt;g++

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;uwsgi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run the following shell command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;build-big

&lt;span class="go"&gt;REPOSITORY          TAG                 IMAGE ID            CREATED                  SIZE&lt;/span&gt;
&lt;span class="go"&gt;blog-python         big                 8a68d0dad407        Less than a second ago   251MB&lt;/span&gt;
&lt;span class="go"&gt;python              3.6-alpine          8eb1c554687d        16 hours ago             90.4MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the “build-big” make target, I’ve included a command to list all Docker images on your system. Because of this command, you should see something close to the following in your terminal:&lt;/p&gt;
&lt;h4 id="the-good"&gt;The good&lt;a class="headerlink" href="#the-good" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The image built successfully.&lt;/p&gt;
&lt;h4 id="the-bad"&gt;The bad&lt;a class="headerlink" href="#the-bad" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The image is unnecessarily large.&lt;/p&gt;
&lt;p&gt;We’re planning on scaling our web-service to handle a decent amount of traffic. Scaling will involve deploying many images on many servers. Larger images take longer to deploy and (obviously) take up more space than smaller images.&lt;/p&gt;
&lt;h4 id="the-ugly"&gt;The ugly&lt;a class="headerlink" href="#the-ugly" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We are including an unnecessary dependency.&lt;/p&gt;
&lt;p&gt;We don’t need a C compiler in the image, so the C compiler is an unnecessary dependency. Including an unnecessary dependency in our runtime image is a horrible design, similar to including an unnecessary Python dependency in our requirements.txt or setup.py. As great software developers, we &lt;span class="caps"&gt;HATE&lt;/span&gt; bad system design, so let’s find a way to resolve the “bad” and the “ugly” while preserving the “good”!&lt;/p&gt;
&lt;h3 id="example-3-failed-attempt-at-simply-uninstalling-c-compiler"&gt;Example 3: failed attempt at simply “uninstalling” C compiler&lt;a class="headerlink" href="#example-3-failed-attempt-at-simply-uninstalling-c-compiler" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, if we want to reduce our image size, we cannot simply “uninstall” the C compiler. For reasons that I do not fully comprehend at this time, Docker caches anything you install in an image, so uninstalling a dependency does &lt;span class="caps"&gt;NOT&lt;/span&gt; reduce the image size.&lt;/p&gt;
&lt;p&gt;Copy the following code into a file called “Dockerfile.uninstall”:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.6-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;bigimage-uninstalled&lt;/span&gt;

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--no-cache&lt;span class="w"&gt; &lt;/span&gt;linux-headers&lt;span class="w"&gt; &lt;/span&gt;g++

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;uwsgi

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apk&lt;span class="w"&gt; &lt;/span&gt;del&lt;span class="w"&gt; &lt;/span&gt;linux-headers&lt;span class="w"&gt; &lt;/span&gt;g++
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run the following shell command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;build-uninstall-big

&lt;span class="go"&gt;REPOSITORY          TAG                 IMAGE ID            CREATED                  SIZE&lt;/span&gt;
&lt;span class="go"&gt;blog-python         big-uninstall       10a0eb5d42aa        Less than a second ago   251MB&lt;/span&gt;
&lt;span class="go"&gt;blog-python         big                 8a68d0dad407        11 minutes ago           251MB&lt;/span&gt;
&lt;span class="go"&gt;python              3.6-alpine          8eb1c554687d        16 hours ago             90.4MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our efforts at removing our C compiler proved futile. At this point, lesser developers would give up and assume we’ve reached the end of the road. But you, dear reader, are reading my blog, and I know you’re better than that! Let’s dig deeper and find an elegant way shrink our Docker image!&lt;/p&gt;
&lt;h3 id="example-4-small-final-build-without-c-compiler"&gt;Example 4: small final build without C compiler&lt;a class="headerlink" href="#example-4-small-final-build-without-c-compiler" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This final example results in a small image with uWSGI installed and without a C compiler. It relies heavily on multi-stage builds and on pip wheels.&lt;/p&gt;
&lt;p&gt;Copy the following code into a file called “Dockerfile.small”:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;###########################################&lt;/span&gt;
&lt;span class="c"&gt;# Throwaway image with C compiler installed&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.6-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;bigimage&lt;/span&gt;

&lt;span class="c"&gt;# install the C compiler&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--no-cache&lt;span class="w"&gt; &lt;/span&gt;linux-headers&lt;span class="w"&gt; &lt;/span&gt;g++

&lt;span class="c"&gt;# instead of installing, create a wheel&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;wheel&lt;span class="w"&gt; &lt;/span&gt;--wheel-dir&lt;span class="o"&gt;=&lt;/span&gt;/root/wheels&lt;span class="w"&gt; &lt;/span&gt;uwsgi

&lt;span class="c"&gt;###########################################&lt;/span&gt;
&lt;span class="c"&gt;# Image WITHOUT C compiler but WITH uWSGI&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.6-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;smallimage&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--from&lt;span class="o"&gt;=&lt;/span&gt;bigimage&lt;span class="w"&gt; &lt;/span&gt;/root/wheels&lt;span class="w"&gt; &lt;/span&gt;/root/wheels

&lt;span class="c"&gt;# Ignore the Python package index&lt;/span&gt;
&lt;span class="c"&gt;# and look for archives in&lt;/span&gt;
&lt;span class="c"&gt;# /root/wheels directory&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;--no-index&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;--find-links&lt;span class="o"&gt;=&lt;/span&gt;/root/wheels&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;uwsgi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run the following shell command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;build-small

&lt;span class="go"&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE&lt;/span&gt;
&lt;span class="go"&gt;blog-python         small               b952f6280b00        1 second ago        97.4MB&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;none&amp;gt;              &amp;lt;none&amp;gt;              91c7bb911f32        3 minutes ago       249MB&lt;/span&gt;
&lt;span class="go"&gt;blog-python         big-uninstall       10a0eb5d42aa        23 minutes ago      251MB&lt;/span&gt;
&lt;span class="go"&gt;blog-python         big                 8a68d0dad407        34 minutes ago      251MB&lt;/span&gt;
&lt;span class="go"&gt;python              3.6-alpine          8eb1c554687d        16 hours ago        90.4MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the image tagged “small” is ~61% smaller than its “big” counterparts. It has 7 additional &lt;span class="caps"&gt;MB&lt;/span&gt; from its base alpine container. These megabytes represent only the uWSGI library itself. We’ll need to make modifications to uWSGI itself to get any smaller. I leave uWSGI modifications as an exercise for the reader.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;a class="headerlink" href="#explanation" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Two key points are responsible for our Docker build’s success:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reliance on copying between image stages in Docker multi-stage builds. This gets around caching problems with a single image&lt;/li&gt;
&lt;li&gt;Understanding the difference between “pip install” and “pip wheel”&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="copying-betwen-docker-build-stages-in-multi-stage-build"&gt;Copying betwen Docker build stages in multi-stage build&lt;a class="headerlink" href="#copying-betwen-docker-build-stages-in-multi-stage-build" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unless we explicitly specify a &lt;a href="https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target"&gt;target&lt;/a&gt;, Docker multi-stage builds will tag their last stage. Downstream build stages can reference upstream build stages and copy resources from them, similarly to how resources can be copied from any local or remote file system into a traditional Docker container. Therefore, we “compile” our Python code in one build stage and copy this compiled code in another build stage. Since the code no longer needs to be compiled, we don’t need to a C compiler or Linux headers. As the coup de grâce, our build’s final stage is not based on any image with a C compiler installed, so this approach completely avoids Docker’s caching complexities.&lt;/p&gt;
&lt;p&gt;Thanks to Docker’s multi-stage builds, we are able to compile our Python package and avoid deploying the build’s system dependencies in our final image.&lt;/p&gt;
&lt;h3 id="difference-between-pip-install-and-pip-wheel"&gt;Difference between “pip install” and “pip wheel”&lt;a class="headerlink" href="#difference-between-pip-install-and-pip-wheel" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Docker multi-stage builds are cool and all, but I’ve seen many articles about them. Python’s packaging tool, &lt;a href="https://pip.pypa.io/en/stable/"&gt;pip&lt;/a&gt;, hasn’t gotten as much careful attention from the blogging community. Hopefully this section can clear up one common point of confusion: &lt;a href="#pip-install"&gt;pip install&lt;/a&gt; vs &lt;a href="#pip-wheel"&gt;pip wheel&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="pip-install"&gt;pip install&lt;a class="headerlink" href="#pip-install" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This is the command most people are familiar with. At a high level, it takes a Python package, runs its setup.py, downloads and installs its dependencies, and potentially does a lot more. Run “pip install” when you want to expand a package’s contents and use it as its author intended.&lt;/p&gt;
&lt;p&gt;A good mental model: “pip install” takes a consolidated bundle of code / build instructions and places the package’s content and dependencies wherever they need to go on an operating system. Once “pip install” runs on our machine, file placement throughout our file system can be pretty &lt;a href="http://stayhawaiian.blogspot.com/2010/05/hamajang.html"&gt;hamajang&lt;/a&gt;, depending on a package’s setup.py instructions.&lt;/p&gt;
&lt;h4 id="pip-wheel"&gt;pip wheel&lt;a class="headerlink" href="#pip-wheel" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This tool is mostly used by library developers wanting to distribute their packages in a user-friendly way. For example, &lt;a href="http://scikit-learn.org"&gt;scikitlearn&lt;/a&gt;, a popular Python library for machine learning, requires &lt;a href="http://scikit-learn.org/stable/developers/advanced_installation.html"&gt;a lot of system dependencies&lt;/a&gt; to build. Many Python users, especially data scientists, are either unwilling or unable to install these dependencies on their host machines. This user-characteristic led to unfortunate platforms like &lt;a href="https://www.anaconda.com/what-is-anaconda/"&gt;Anaconda&lt;/a&gt; (author opinion). On a more mature note, for those of us with the appropriate dependencies installed, the installation process would often take a very long time; C, &lt;span class="caps"&gt;FORTRAN&lt;/span&gt;, and possibly other languages each needed to be compiled, and installing code written in these languages often leads to a long coffee break.&lt;/p&gt;
&lt;p&gt;Wheels enable Python developers to compile a package, and its dependencies, in a distributable form targeting common operating system architectures. Today, most scikitlearn users install it &lt;a href="http://scikit-learn.org/stable/install.html"&gt;using its wheel&lt;/a&gt;, which takes a fraction of the time of the regular build process.&lt;/p&gt;
&lt;p&gt;A good mental model: “pip wheel” takes a Python package, makes it ready to be installed on any target machine &lt;span class="caps"&gt;WITHOUT&lt;/span&gt; its build dependencies, and puts it in &lt;span class="caps"&gt;ONE&lt;/span&gt; easily-distributed archive file.&lt;/p&gt;
&lt;h4 id="why-we-care-about-this"&gt;Why we care about this?&lt;a class="headerlink" href="#why-we-care-about-this" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Not all Python packages are distributed as wheels. There are some packages, based mostly on C, that are hard to compile once and use in many places. uWSGI appears to be one of those packages. To build our final image, we construct a throw-away container to construct a wheel for uWSGI.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="headerlink" href="#conclusion" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When building a Docker container for a Python application, we can install packages requiring build-time system dependencies &lt;span class="caps"&gt;AND&lt;/span&gt; remove these system dependencies from our final Docker image through a combination of Docker multi-stage builds, pip wheel, and pip install.&lt;/p&gt;
&lt;h2 id="special-thanks"&gt;Special thanks&lt;a class="headerlink" href="#special-thanks" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This post took inspiration from &lt;a href="https://lekum.org/post/multistage-dockerfile/"&gt;this post&lt;/a&gt; by Alejandro Guirao. I am indebted to Alejandro for publishing his creative use of docker multi-stage builds in the context of Python systems.&lt;/p&gt;</content><category term="Software Development"></category><category term="docker"></category><category term="python"></category><category term="pip"></category></entry><entry><title>Vim line numbers</title><link href="https://samroeca.com/vim-line-numbers.html" rel="alternate"></link><published>2018-04-07T00:00:00-05:00</published><updated>2018-04-07T00:00:00-05:00</updated><author><name>Samuel Roeca</name></author><id>tag:samroeca.com,2018-04-07:/vim-line-numbers.html</id><summary type="html">&lt;p&gt;Learn how to make your Vim line numbers relative and/or not relative at optimal&amp;nbsp;times.&lt;/p&gt;</summary><content type="html">&lt;div class="toc"&gt;&lt;span class="toctitle"&gt;Table of Contents&lt;/span&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-problem"&gt;The Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#wrapping-my-own-solution"&gt;Wrapping my own solution&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-good"&gt;The good&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-bad"&gt;The bad&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-back-breaking-straw"&gt;The back-breaking straw&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-game-changing-plugin"&gt;The Game-Changing Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;If you want your Vim line numbers to be relative and/or not relative at the correct times, I recommend installing the &lt;a href="https://github.com/myusuf3/numbers.vim"&gt;myusuf3/numbers.vim&lt;/a&gt; plugin.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;a class="headerlink" href="#the-problem" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When editing text in a Vim window, I use relative numbers to help me use motions across text relative to my cursor. However, when I have multiple windows open, relative numbers look pretty weird in windows that I am not currently editing. It would be nice for Vim to intelligently alternate between relativenumber and norelativenumber based on my Vim cursor location. Base Vim does not have this capability, so we have three options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Accept a suboptimal workflow&lt;/li&gt;
&lt;li&gt;Wrap our own solution in our .vimrc&lt;/li&gt;
&lt;li&gt;Find a Plugin&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I lived with option 1 for a while, but eventually grew too annoyed. I then tried researching plugins, but thought I understood the problem well-enough to write my own solution. So I went straight to option 2 and tried wrapping my own solution.&lt;/p&gt;
&lt;h2 id="wrapping-my-own-solution"&gt;Wrapping my own solution&lt;a class="headerlink" href="#wrapping-my-own-solution" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following code represents my original solution:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; ToggleRelativeNumber&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &amp;amp;&lt;span class="nb"&gt;rnu&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;norelativenumber&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; RNUInsertEnter&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &amp;amp;&lt;span class="nb"&gt;rnu&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'rnu'&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;norelativenumber&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nornu'&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; RNUInsertLeave&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'rnu'&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;norelativenumber&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nornu'&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; RNUWinEnter&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; exists&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'w:line_number_state'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'rnu'&lt;/span&gt;
      &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;norelativenumber&lt;/span&gt;
    &lt;span class="k"&gt;endif&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'rnu'&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; RNUWinLeave&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &amp;amp;&lt;span class="nb"&gt;rnu&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'rnu'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:line_number_state &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nornu'&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
  &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;norelativenumber&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;

&lt;span class="c"&gt;" autocmd that will set up the w:created variable&lt;/span&gt;
autocmd &lt;span class="nb"&gt;VimEnter&lt;/span&gt; * autocmd &lt;span class="nb"&gt;WinEnter&lt;/span&gt; * &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:created&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
autocmd &lt;span class="nb"&gt;VimEnter&lt;/span&gt; * &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;w&lt;/span&gt;:created&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;number&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
augroup rnu_nu
  autocmd&lt;span class="p"&gt;!&lt;/span&gt;
&lt;span class="c"&gt;  "Initial window settings&lt;/span&gt;
  autocmd &lt;span class="nb"&gt;WinEnter&lt;/span&gt; * &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;exists&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'w:created'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
        \&lt;span class="k"&gt;setlocal&lt;/span&gt; &lt;span class="k"&gt;number&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
        \&lt;span class="k"&gt;endif&lt;/span&gt;
  autocmd &lt;span class="nb"&gt;User&lt;/span&gt; Startified &lt;span class="k"&gt;setlocal&lt;/span&gt; &lt;span class="k"&gt;number&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
&lt;span class="c"&gt;  " Don't have relative numbers during insert mode&lt;/span&gt;
  autocmd &lt;span class="nb"&gt;InsertEnter&lt;/span&gt; * :&lt;span class="k"&gt;call&lt;/span&gt; RNUInsertEnter&lt;span class="p"&gt;()&lt;/span&gt;
  autocmd &lt;span class="nb"&gt;InsertLeave&lt;/span&gt; * :&lt;span class="k"&gt;call&lt;/span&gt; RNUInsertLeave&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;  " Set and unset relative numbers when buffer is active&lt;/span&gt;
  autocmd &lt;span class="nb"&gt;WinEnter&lt;/span&gt; * :&lt;span class="k"&gt;call&lt;/span&gt; RNUWinEnter&lt;span class="p"&gt;()&lt;/span&gt;
  autocmd &lt;span class="nb"&gt;WinLeave&lt;/span&gt; * :&lt;span class="k"&gt;call&lt;/span&gt; RNUWinLeave&lt;span class="p"&gt;()&lt;/span&gt;
augroup &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="the-good"&gt;The good&lt;a class="headerlink" href="#the-good" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The solution worked for most windows and tabs, most of the time.&lt;/p&gt;
&lt;h3 id="the-bad"&gt;The bad&lt;a class="headerlink" href="#the-bad" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The code is a bit involved and it takes a little time to explain to others.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It relies on window-local variables (w:line_number_state, etc). These exacerbate Vim’s already-difficult state-management woes.&lt;/li&gt;
&lt;li&gt;Several global functions are defined&lt;/li&gt;
&lt;li&gt;There are some quirks I don’t fully understand around the creation of variables during Vim startup (hence lines 50 and 51).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Despite these mild downsides, I was pretty proud that the solution mostly worked. That is, until I wasn’t.&lt;/p&gt;
&lt;h3 id="the-back-breaking-straw"&gt;The back-breaking straw&lt;a class="headerlink" href="#the-back-breaking-straw" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My custom solution did not work appropriately with some of my plugins. Namely, it didn’t play well with &lt;a href="https://github.com/majutsushi/tagbar"&gt;majutsushi/tagbar&lt;/a&gt;, which I use frequently enough for this feature-dearth to become royally annoying. Therefore, after learning the ins-and-outs of window-specific variables and every Vim autocmd, I went back to the Plugin ecosystem to see if I’d missed anything…&lt;/p&gt;
&lt;h2 id="the-game-changing-plugin"&gt;The Game-Changing Plugin&lt;a class="headerlink" href="#the-game-changing-plugin" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Turns out a wonderful developer already solved this problem for me. Assuming you use &lt;a href="https://github.com/junegunn/vim-plug"&gt;junegunn/vim-plug&lt;/a&gt; to manage your plugins, place the following code in your .vimrc&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.vim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;" Relative Numbering&lt;/span&gt;
Plug &lt;span class="s1"&gt;'myusuf3/numbers.vim'&lt;/span&gt;
&lt;span class="c"&gt;" Put the rest of your plugins below...&lt;/span&gt;
&lt;span class="k"&gt;call&lt;/span&gt; plug#&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;" Now, exclude the plugins you don't want numbers to deal with&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;:numbers_exclude &lt;span class="p"&gt;=&lt;/span&gt; [&lt;span class="s1"&gt;'startify'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gundo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'vimshell'&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will give you a great editing experience. See below for a screencast:&lt;/p&gt;
&lt;p&gt;&lt;img alt="numbers.vim screencast" class="align-center" src="https://samroeca.com/gif/numbers-vim.gif"/&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="headerlink" href="#conclusion" title="Permanent link"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Numbers.vim provides a usable line-numbering solution with minimal required configuration. I regret nothing about my bespoke journey, but I’m glad that Numbers.vim is my destination.&lt;/p&gt;</content><category term="Software Development"></category><category term="vim"></category><category term="neovim"></category></entry></feed>