<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam Parkin</title>
    <description>The latest articles on DEV Community by Adam Parkin (@pzelnip).</description>
    <link>https://dev.to/pzelnip</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F11676%2Fd09bda53-8f23-4d43-8807-091ce6c9b37c.jpg</url>
      <title>DEV Community: Adam Parkin</title>
      <link>https://dev.to/pzelnip</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pzelnip"/>
    <language>en</language>
    <item>
      <title>Using namedtuple's to convert dicts to objects</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Fri, 25 Aug 2023 22:06:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/using-namedtuples-to-convert-dicts-to-objects-453l</link>
      <guid>https://dev.to/pzelnip/using-namedtuples-to-convert-dicts-to-objects-453l</guid>
      <description>&lt;p&gt;A friend shared this with me today and I thought it was pretty neat. If you have a dict, and you want to convert it to an object where the object properties are the keys from the dict, and the values are the values from the dict, you can use a namedtuple to do so. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; some_dict = {"name": "My name", "func" : "my func"}
&amp;gt;&amp;gt;&amp;gt; import namedtuple
&amp;gt;&amp;gt;&amp;gt; SomeClass = namedtuple("SomeClass", some_dict.keys())
&amp;gt;&amp;gt;&amp;gt; as_an_object = SomeClass(**some_dict)
&amp;gt;&amp;gt;&amp;gt; as_an_object.name
'My name'
&amp;gt;&amp;gt;&amp;gt; as_an_object.func
'my func'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Won't handle nested dicts (the sub-dicts will still be dicts on the constructed object), but for a quick and dirty way to convert a dict to an object, this seems pretty handy.&lt;/p&gt;

&lt;p&gt;Using the splat operator you can also save a line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; as_an_object = namedtuple("SomeClass", some_dict.keys())(**some_dict)
&amp;gt;&amp;gt;&amp;gt; as_an_object
SomeClass(name='My name', func='my func')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>dict</category>
      <category>namedtuple</category>
    </item>
    <item>
      <title>F-Strings Are F'ing Cool Part 2</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sun, 11 Jul 2021 22:27:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/f-strings-are-f-ing-cool-part-2-5fn</link>
      <guid>https://dev.to/pzelnip/f-strings-are-f-ing-cool-part-2-5fn</guid>
      <description>&lt;p&gt;So in a &lt;a href="https://dev.to/pzelnip/f-strings-are-fing-cool-5539-temp-slug-4002218"&gt;previous post&lt;/a&gt; a couple years ago I wrote about some of the reasons the f-string functionality introduced in Python 3.6 was so useful. Today I learned a few new tricks with them from &lt;a href="https://pythonbytes.fm/episodes/show/241/f-yes-we-want-some-f-string-tricks"&gt;a recent episode of the Python Bytes Podcast&lt;/a&gt; so wanted to revisit the topic &amp;amp; share.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging
&lt;/h2&gt;

&lt;p&gt;So how many times have you been debugging, wanted to know what the value of a variable was at a particular point, and then wrote something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"The value of myvariable is: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;myvariable&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, putting aside the fact I'm using print statements for debugging (😱), this is extremely common. But what if you had a second variable? Ok, so then you add it as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myothervariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"The value of myvariable is: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;myvariable&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; myothervariable: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;myothervariable&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="n"&gt;myothervariable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so on. But I'm lazy, I don't want to repeat the variable name twice, so as of Python 3.8 there's a &lt;a href="https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging"&gt;new specifier to aid with debugging&lt;/a&gt;. By appending an equal sign (&lt;code&gt;=&lt;/code&gt;) to the expression the both the name &amp;amp; value get emitted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myothervariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"The value of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;myvariable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;myothervariable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;myvariable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="n"&gt;myothervariable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Super handy. What's really crazy is it works with expressions too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myothervariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"Calculation: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;myothervariable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Calculation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myvariable&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;myothervariable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.3327957575757574&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be handy when printing out properties of an object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;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;Foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"&lt;/span&gt;&lt;span class="si"&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;bar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&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;bar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also particularly handy if you have a function which takes arbitrary parameters via &lt;code&gt;args&lt;/code&gt; and &lt;code&gt;kwargs&lt;/code&gt; and you want to see what the values of them are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&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="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2342&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"adlfk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;9234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dfslkj"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2342&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'adlfk'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'arg1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'arg2'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'dfslkj'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Format Specifiers
&lt;/h2&gt;

&lt;p&gt;So one of the things I talked about in my last post on F-strings was how you can use all the same format specifiers you used to use with &lt;code&gt;.format()&lt;/code&gt; with F-strings. A common example of this is to format a float to a specific number of decimal places:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"Pi to 3 digits: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;:.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Pi&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.142&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or formatting &lt;code&gt;datetimes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"Today is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Today&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;July&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is neat and all, but oftentimes these format strings are used in many places in a project. Say for example you want all dates in your project to be formatted like the example above. One approach would be to repeat that format string (&lt;code&gt;%B %d, %Y&lt;/code&gt;) in each place you format a date. The problem though is if that format changes (say in the future you want 2 digit years, or want to include the day of the week, etc) you'd then have to search &amp;amp; replace every spot where you've used that format.&lt;/p&gt;

&lt;p&gt;Ok, so this is software engineering 101, time for the "Don't Repeat Yourself" (or DRY) principle to apply, so maybe you extract out a function to format a datetime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime_obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime_obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"Today is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;format_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Today&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;July&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which works and is perfectly reasonable, but now you have this little function floating around which makes the f-string expression a little more cumbersome. One could also make a case that defining a function that is a single line of code is a bit overengineered (I'm not particularly sympathetic to this view myself, but some are). And really the format string is a constant, so arguably it's a little weird to have a function to express it's application.&lt;/p&gt;

&lt;p&gt;But, here's where my mind was blown -- you can nest f-string expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DATETIME_FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'%B %d, %Y'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&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="s"&gt;"Today is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DATETIME_FORMAT&lt;/span&gt;&lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Today&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;July&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our format expression is a single constant variable, and we just defer to it wherever we want to format a &lt;code&gt;datetime&lt;/code&gt;. If that format changes, we simply update the constant. Really useful trick.&lt;/p&gt;

</description>
      <category>posts</category>
      <category>python</category>
      <category>pythontipoftheday</category>
      <category>fstrings</category>
    </item>
    <item>
      <title>VS Code Tip Of The Day - Selectively Adding Files To A Git Commit</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sat, 03 Apr 2021 17:26:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/vs-code-tip-of-the-day-selectively-adding-files-to-a-git-commit-40j6</link>
      <guid>https://dev.to/pzelnip/vs-code-tip-of-the-day-selectively-adding-files-to-a-git-commit-40j6</guid>
      <description>&lt;p&gt;Small tip: sometimes when working on something I find myself making changes to many files. I then only want to include a few of those files in my next Git commit. To date I've always just manually done a &lt;code&gt;git add &amp;lt;file&amp;gt;&lt;/code&gt; in the terminal for each file I want to add, but this is tedious if there's many of them.&lt;/p&gt;

&lt;p&gt;Turns out there's an easy way to do this in VS Code. If you go to the Source Control item on the left nav (the icon that looks like a branch), you’ll see a list of all untracked and modified files. For example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ssZvgk01--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/source-control-min.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ssZvgk01--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/source-control-min.png" alt="Showing Modified Files in the Source Control View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this screenshot you can see I have a number of files that have been modified (the ones with the "M" beside them) and two new (untracked) files (the ones with the "U" beside them).&lt;/p&gt;

&lt;p&gt;If you want to see what's changed in any of the modified files, clicking the item will bring up a diff window. If you then want to include (or "stage") this file for the next commit, right-click it and pick "Stage Changes":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6ZmedFR4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/stagechanges-min.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6ZmedFR4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/stagechanges-min.png" alt="Selecting Stage Changes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat for each file you want to include, and then do a &lt;code&gt;git commit&lt;/code&gt; to complete the commit. Alternatively if you're anti-terminal you can click the checkmark on this same view to complete the commit.&lt;/p&gt;

</description>
      <category>posts</category>
      <category>git</category>
      <category>vscode</category>
    </item>
    <item>
      <title>The Wild World of Apple Silicon</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sat, 06 Mar 2021 18:33:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/the-wild-world-of-apple-silicon-njb</link>
      <guid>https://dev.to/pzelnip/the-wild-world-of-apple-silicon-njb</guid>
      <description>&lt;p&gt;I recently took the plunge and obtained a shiny new M1-powered Macbook Air. For those unfamiliar, last year Apple announced that they were now building machines with a brand new ARM-based architecture, making the switch from the long-lived x86 Intel architecture. This brought promises of amazing battery life, amazing performance, and terrifying compatibility issues. Now that I've been living with this machine for a week or two, I thought I'd recap my experience both setting it up, any gotchas or surprises along the way, as well as my experiences around how well the new architecture works as experienced through the lens of a developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 1: Rosetta Works, But Sucks Battery Like You Wouldn't Believe
&lt;/h2&gt;

&lt;p&gt;Everything I've run through Rosetta has been flawless from a functionality perspective. Having said that though: anything run through Rosetta does seem to suck battery life. And not just apps that are normally CPU intensive. For example: I found that having Dropbox (which doesn't support M1), Itsycal, and Spectacle constantly running in my menu bar all seemed to have a significant drain on battery life. I've since switched from Dropbox to &lt;a href="https://www.sync.com/"&gt;Sync&lt;/a&gt;, from Spectacle to &lt;a href="https://rectangleapp.com/"&gt;Rectangle&lt;/a&gt;, and have uninstalled Itsycal as I still haven't found an M1-powered replacement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 2: Which Apps Are M1 Ready is Really Random
&lt;/h2&gt;

&lt;p&gt;So of these, which would you expect are M1 ready right now?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slack&lt;/li&gt;
&lt;li&gt;Chrome&lt;/li&gt;
&lt;li&gt;Firefox&lt;/li&gt;
&lt;li&gt;Visual Studio Code&lt;/li&gt;
&lt;li&gt;Sublime Text&lt;/li&gt;
&lt;li&gt;Dropbox&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you answered the first three, then kudos to you, though until very recently (ie within the last week or so) VS Code only had M1 support via Insiders. It looks like &lt;a href="https://forum.sublimetext.com/t/apple-silicon-native-build/54775"&gt;Sublime Text 3 will &lt;em&gt;never&lt;/em&gt; support M1&lt;/a&gt; and ST4 is still a long ways off, which for a paid product used by &lt;em&gt;a lot&lt;/em&gt; of Mac users is truly mind-blowing to me. The fact that Dropbox still doesn't have M1 support is just inexcusable at this point (particularly given it's an "always running" app). Docker has a preview version that's been out for some time, but full support still seems like a long ways off. Side note: I haven't tried the preview version, and I don't plan on it as there's been mixed reports on how stable it is (&lt;a href="https://blog.earthly.dev/using-apple-silicon-m1-as-a-cloud-engineer-two-months-in/"&gt;a positive take&lt;/a&gt; and &lt;a href="https://twitter.com/mkennedy/status/1360318443661107210"&gt;a less positive take&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 3: Homebrew is Ready, but Your Obscure Package Might Not Be
&lt;/h2&gt;

&lt;p&gt;With Homebrew 3.0, the popular package manager is now M1-ready. I can happily report that the vast majority of packages I use are M1-native. I installed &lt;code&gt;python3&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;git-extras&lt;/code&gt;, &lt;code&gt;bash-completion&lt;/code&gt;, &lt;code&gt;pyenv&lt;/code&gt;, &lt;code&gt;pipx&lt;/code&gt;, &lt;code&gt;starship&lt;/code&gt;, &lt;code&gt;the_silver_searcher&lt;/code&gt;, &lt;code&gt;hugo&lt;/code&gt;, &lt;code&gt;watch&lt;/code&gt; and a bunch of others without issue, and all seem to be M1 as reported in Activity Monitor.&lt;/p&gt;

&lt;p&gt;So what happens when something isn't?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ brew install hadolint

Updating Homebrew...
==&amp;gt; Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==&amp;gt; New Formulae
bas55 delve geph4 kotlin-language-server latino libpipeline openmodelica oras sqlancer
==&amp;gt; Updated Formulae
Updated 267 formulae.

Error: hadolint: no bottle available!
You can try to install from source with:
  brew install --build-from-source hadolint
Please note building from source is unsupported. You will encounter build
failures with some formulae. If you experience any issues please create pull
requests instead of asking for help on Homebrew's GitHub, Twitter or any other
official channels.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I asked &lt;a href="https://github.com/Homebrew/brew/issues/10744"&gt;about this on Github&lt;/a&gt; and since &lt;code&gt;hadolint&lt;/code&gt; is built with &lt;code&gt;ghc&lt;/code&gt; and &lt;code&gt;ghc&lt;/code&gt; isn't M1 ready (and likely won't be for some time) you either live without the package, install a separate Rosetta-based brew installation, or obtain the package from some other means (in the case of &lt;code&gt;hadolint&lt;/code&gt; this is what I did: there are &lt;a href="https://github.com/hadolint/hadolint/releases/tag/v1.23.0"&gt;self-contained binaries on their Github&lt;/a&gt;, so I &lt;a href="https://github.com/hadolint/hadolint/issues/558"&gt;threw the latest in my path&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;One pro-tip: this site is awesome for seeing if a package you're interested in is M1-ready or not: &lt;a href="https://doesitarm.com/kind/homebrew/"&gt;https://doesitarm.com/kind/homebrew/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 4: Homebrew Is Different Now
&lt;/h2&gt;

&lt;p&gt;One minor gotcha I ran into is that Brew installs to a different directory: &lt;code&gt;/opt/homebrew&lt;/code&gt;. If you have scripts (think things like &lt;code&gt;.bashrc&lt;/code&gt; &amp;amp; the like) that reference the old brew path they'll have to be tweaked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 5: Instant On is Amazing
&lt;/h2&gt;

&lt;p&gt;This is a minor thing, and honestly I didn't think I'd like it as much as I do, but M1 Macbooks feature an "instant on" wake up from sleep. And it truly is "instant". Ie before I've completely opened the lid of my MBA the screen is already on and awaiting input. This even happens when I have an external display connected. Contrast this with my Intel-based Macbook Pro for work which takes a good 30 seconds to resume from sleep (often longer if I have external displays connected).&lt;/p&gt;

&lt;p&gt;Surprisingly this meant I didn't bother installing &lt;a href="https://apps.apple.com/us/app/amphetamine/id937984704?mt=12"&gt;Amphetamine&lt;/a&gt; on this machine since there's no point -- I don't care if my M1 Mac falls asleep as it wakes up so damn fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 6: Big Sur Is Less Good
&lt;/h2&gt;

&lt;p&gt;This is less dev-orientated, but I really don't like Big Sur. The new Notification Center is annoying and just wastes space on my menubar. Toast notifications look much bigger on screen (so are more jarring). Lots of little annoyances with it, none of which are dealbreaking, but if I had my way I'd have Catalina instead of Big Sur on this machine (alas, not an option).&lt;/p&gt;

&lt;h2&gt;
  
  
  In Summary
&lt;/h2&gt;

&lt;p&gt;This machine is awesome. It's expensive (as all Macs are), but is crazy fast, and (once you get rid of all your Intel apps) sips battery very lightly.&lt;/p&gt;

&lt;p&gt;Docker is really the only thing that I miss at this point from turning this into a real dev machine. Hopefully full M1 support will arrive for that though I can't help but wonder if Docker will ever be completely compatible (if you build a Docker image on M1, can you run that image on an Intel based machine?)&lt;/p&gt;

</description>
      <category>apple</category>
      <category>m1</category>
      <category>applesilicon</category>
    </item>
    <item>
      <title>Using Starship For Terminal Prompt Goodness</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sat, 27 Feb 2021 18:39:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/using-starship-for-terminal-prompt-goodness-1d0</link>
      <guid>https://dev.to/pzelnip/using-starship-for-terminal-prompt-goodness-1d0</guid>
      <description>&lt;p&gt;Recently I had the good fortune of attending the &lt;a href="https://2021.pycascades.com/" rel="noopener noreferrer"&gt;2021 iteration of the Pycascades conference&lt;/a&gt;, and there was at least one talk that mentioned &lt;a href="https://starship.rs/" rel="noopener noreferrer"&gt;Starship&lt;/a&gt; and at least one other where the presenter happened to be using it. For those not in the know, Starship is a cross-shell compatible terminal prompt generator written in Rust that is crazy fast and crazy customizable.&lt;/p&gt;

&lt;p&gt;I was intrigued, and decided to take my (reasonably sophisticated) Bash prompt and Starship-ify it. In this post I'll outline some of the things I went through, lessons learned, and hopefully impart some advice on how to do things with it.&lt;/p&gt;

&lt;p&gt;To begin with, here was my old bash prompt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2Foldprompt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2Foldprompt.png" alt="My Old Prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That prompt has a bunch of things in it, and shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exit code of the last command (0 in this case)&lt;/li&gt;
&lt;li&gt;the current day &amp;amp; time, along with the TZ info&lt;/li&gt;
&lt;li&gt;the time since I last rebooted (2 days in this case)&lt;/li&gt;
&lt;li&gt;my current directory&lt;/li&gt;
&lt;li&gt;the name of the current branch I’m on (&lt;code&gt;starship&lt;/code&gt; in this case) and the asterisk to indicate there’s uncommitted changes&lt;/li&gt;
&lt;li&gt;The lambda symbol as my input symbol (not a Half-Life reference, more a reference to my functional programming days)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not shown in that screenshot is that when I have a Python virtual environment activated it also gets displayed in the prompt.&lt;/p&gt;

&lt;p&gt;The "code" for this that resided in my &lt;code&gt;.bashrc&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function uptimeinfo {
    uptime | perl -ne 'if(/\d\s+up(.\*),\s+\d+\s+users/) { $s = $1; $s =~ s/^\s+|\s+$//g; print $s; }'
}

function proml {
  local BLACK="\[\033[0;30m\]"
  local GRAY="\[\033[1;30m\]"
  local RED="\[\033[0;31m\]"
  local LIGHT\_RED="\[\033[1;31m\]"
  local GREEN="\[\033[0;32m\]"
  local LIGHT\_GREEN="\[\033[1;32m\]"
  local BROWN="\[\033[0;33m\]"
  local YELLOW="\[\033[1;33m\]"
  local BLUE="\[\033[0;34m\]"
  local LIGHT\_BLUE="\[\033[1;34m\]"
  local PURPLE="\[\033[0;35m\]"
  local LIGHT\_PURPLE="\[\033[1;35m\]"
  local CYAN="\[\033[0;36m\]"
  local LIGHT\_CYAN="\[\033[1;36m\]"
  local LIGHT\_GRAY="\[\033[0;37m\]"
  local WHITE="\[\033[1;37m\]"
  case $TERM in
    xterm*)
    TITLEBAR='\[\033]0;\u@\h:\w \D{%a %b %d %Y %l:%M%p (%Z%z)}\007\]'
    ;;
    *)
    TITLEBAR=""
    ;;
  esac

PCOLOR="\[\033[\$(promptcol)\]"

# note that in the following prompt the error code item (\$?) must be the
# first item in the prompt. Otherwise it'll show the errorcode for the last
# command executed in producing the prompt.
PS1="${TITLEBAR}\
$BLUE [$GREEN[\$?] [\D{%a %b %d %Y %l:%M%p (%Z%z)}] [Up: \$(uptimeinfo)] $BROWN\u@\h:\w $LIGHT\_GRAY\$(\_\_git\_ps1)\
$BLUE]\
\n$PCOLOR λ $LIGHT\_GRAY"
PS2='&amp;gt; '
PS4='+ '
}
proml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a lot, and to be honest, I might be missing some of the code as &lt;a href="https://github.com/pzelnip/dotfiles/blob/mainline/dotfiles/.bashrc" rel="noopener noreferrer"&gt;my .bashrc file&lt;/a&gt; is full of random little snippets.&lt;/p&gt;

&lt;p&gt;It's also shell-specific. If I ever wanted to move to Zsh, or Fish, or whatever, I'd have to re-invent that (and this has been an impediment for me to switching shells).&lt;/p&gt;

&lt;p&gt;So I took this as a starting point and wanted to recreate it in Starship. Starting point was to install Starship with Brew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install starship

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy enough. Next is to initialize it in your shell of choice. In my case for Bash this was adding the following to my &lt;code&gt;.bashrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eval "$(starship init bash)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are equivalent instructions on the &lt;a href="https://starship.rs/" rel="noopener noreferrer"&gt;Starship website&lt;/a&gt; for other prompts&lt;/p&gt;

&lt;p&gt;Lastly, you need a &lt;code&gt;~/.config/starship.toml&lt;/code&gt; file where you'll configure your prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch ~/.config/starship.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open up a new terminal and you should see the default Starship prompt which is actually quite sophisticated out of the box:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2Fdefaultstarship.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2Fdefaultstarship.png" alt="Default Starship Prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your output may vary, as many of the items in a Starship prompt are dynamic depending on your current context. In this you can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'm in a directory called &lt;code&gt;dotfiles&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;I'm currently in a Git repo, but not on any branch instead checked out an arbitrary commit with SHA &lt;code&gt;931e5c4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The coffee cup has to do with Java, but I don't have a valid JDK installed so it's not showing what version&lt;/li&gt;
&lt;li&gt;My current Python environment is Python 3.9.2&lt;/li&gt;
&lt;li&gt;My AWS environment is configured to communicate with the &lt;code&gt;ca-central-1&lt;/code&gt; region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot! Note that each of those things is what Starship calls a &lt;em&gt;Module&lt;/em&gt;. This is one of the key things about Starship is that each part of your prompt is made up by a distinct module that you configure. So that prompt is currently displaying the &lt;a href="https://starship.rs/config/#directory" rel="noopener noreferrer"&gt;Directory Module&lt;/a&gt;, the &lt;a href="https://starship.rs/config/#git-branch" rel="noopener noreferrer"&gt;Git Branch Module&lt;/a&gt;, the &lt;a href="https://starship.rs/config/#git-commit" rel="noopener noreferrer"&gt;Git Commit Module&lt;/a&gt;, the &lt;a href="https://starship.rs/config/#python" rel="noopener noreferrer"&gt;Python Module&lt;/a&gt;, and the &lt;a href="https://starship.rs/config/#aws" rel="noopener noreferrer"&gt;AWS Module&lt;/a&gt;. Technically it's showing a few others, but this gives you an idea of how Starship &lt;em&gt;composes&lt;/em&gt; your prompt by stringing together some modules.&lt;/p&gt;

&lt;p&gt;The full list of all modules can be found at: &lt;a href="https://starship.rs/config/" rel="noopener noreferrer"&gt;https://starship.rs/config/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, lets get started trying to configure this prompt to be like my old one. I opened up my &lt;code&gt;starship.toml&lt;/code&gt; and added the contents from the example on the Starship docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Don't print a new line at the start of the prompt
add\_newline = false

# Replace the "❯" symbol in the prompt with "➜"
[character] # The name of the module we are configuring is "character"
success\_symbol = "[➜](bold green)" # The "success\_symbol" segment is being set to "➜" with the color "bold green"

# Disable the package module, hiding it from the prompt completely
[package]
disabled = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this you probably won't see much change, aside from the symbol changing from a &lt;code&gt;&amp;gt;&lt;/code&gt; to a &lt;code&gt;➜&lt;/code&gt; due to the configuration of the &lt;code&gt;character&lt;/code&gt; module. See, everything in a Starship prompt (including the symbol at the end) is a module. Well, mostly. 😜&lt;/p&gt;

&lt;p&gt;Ok, so it currently shows the directory, but not like my old prompt where A) it was a yellow-ish colour, and B) showed the full path. Looking at the docs for the Directory module, I gave this a try to configure it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[directory]
truncation\_length = 100
truncate\_to\_repo = false
style = "yellow"
format = "[:$path]($style)[$read\_only]($read\_only\_style) "
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explain this a little bit to give a feel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;truncation_length&lt;/code&gt; controls how many directories deep you have to be before Starship will abbreviate the directory name in your prompt. I rarely go very deep and to be honest when I do I still want to see the full path so I made the number rediculously high so that it never truncated&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;truncate_to_repo&lt;/code&gt; is a special setting that controls if the directory is truncated to the root of the Git repo you are currently in. Again, I don't like this (I want to see the full path), so disabled it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;style&lt;/code&gt; is a common setting on (I believe) every module and controls the colour of the module when rendered. In this case saying "yellow" to match my old prompt. Styles are covered in depth in the &lt;a href="https://starship.rs/advanced-config/#style-strings" rel="noopener noreferrer"&gt;docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;format&lt;/code&gt; is another common setting across every module and controls effectively the "layout" of the module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's worth digging into the &lt;code&gt;format&lt;/code&gt; directive there, as understanding this goes a long way in understanding how you control Starship's output. The expression:&lt;code&gt;"[:$path]($style)[$read_only]($read_only_style) "&lt;/code&gt; says start a &lt;em&gt;text group&lt;/em&gt;(this specified by the &lt;code&gt;[&lt;/code&gt; and &lt;code&gt;]&lt;/code&gt; delimiters) and have it output a colon (&lt;code&gt;:&lt;/code&gt;) followed by the value of the &lt;code&gt;$path&lt;/code&gt; variable. Each module has its own set of variables that get populated with values that are relevant to that module (in this case &lt;code&gt;$path&lt;/code&gt; ends up being the full path of the current working directory). The brackets that follow a text group specify a &lt;code&gt;style string&lt;/code&gt;. You might wonder "but isn't that what the &lt;code&gt;style&lt;/code&gt; setting is for?" And yes, but essentially the &lt;code&gt;style&lt;/code&gt; setting defines the "default" style within a module, and style strings within the format can override that. In this case, &lt;code&gt;$style&lt;/code&gt;corresponds to the &lt;code&gt;style&lt;/code&gt; setting defined in the configuration for the module (in this case "yellow"). You can see that later in this definition I have the &lt;code&gt;$read_only&lt;/code&gt; variable (which gets displayed when the current working directory is read only) and has a different style defined for that scenario (the default &lt;code&gt;$read_only_style&lt;/code&gt; is "red", but you could change that in this configuration by adding a &lt;code&gt;read_only_style="blue"&lt;/code&gt; setting to the directory config). In any case the relevant part of the docs on format strings is &lt;a href="https://starship.rs/config/#format-strings" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Clear as mud? Admittedly, this does take a bit to get your head around (or did for me), but basically you &lt;em&gt;override&lt;/em&gt; settings in the config as appropriate to tweak each module to your liking.&lt;/p&gt;

&lt;p&gt;Ok, that's fine, but Adam how do we control the order of items in the prompt? And that's a good question that took me a little while to figure out. Turns out that the prompt as a whole has a &lt;code&gt;format&lt;/code&gt; setting. The default is to show all modules, this is from the docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;format = "$all"

# Which is equivalent to
format = """
$username\
$hostname\
$shlvl\
$kubernetes\
$directory\
$git\_branch\
$git\_commit\
$git\_state\
$git\_status\
$hg\_branch\
$docker\_context\
$package\
$cmake\
$dart\
$dotnet\
$elixir\
$elm\
$erlang\
$golang\
$helm\
$java\
$julia\
$kotlin\
$nim\
$nodejs\
$ocaml\
$perl\
$php\
$purescript\
$python\
$ruby\
$rust\
$swift\
$terraform\
$vagrant\
$zig\
$nix\_shell\
$conda\
$memory\_usage\
$aws\
$gcloud\
$openstack\
$env\_var\
$crystal\
$custom\
$cmd\_duration\
$line\_break\
$lua\
$jobs\
$battery\
$time\
$status\
$character"""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what controls the order of modules. Move an item up, and it'll appear earlier in the prompt, move down to move it later in the prompt. Personally I don't like this, as it means if you want to change the order of items you have to override the &lt;em&gt;entire&lt;/em&gt; format string. It'd be nice if there was an "index" value or something on each module that could determine ordering, but oh well.&lt;/p&gt;

&lt;p&gt;In any case it also provides a global "completely hide" ability for a module -- if you remove it from the format then it won't be displayed. Note that I don't think hiding from the format is the same thing as &lt;em&gt;disabling&lt;/em&gt; a module. Each module has a &lt;code&gt;disabled&lt;/code&gt; setting which (if true) disables that module (so won't get displayed, and I believe not evaluated).&lt;/p&gt;

&lt;p&gt;Ok, with this I continued on and got most of my old prompt in place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Don't print a new line at the start of the prompt
add\_newline = false

[character]
success\_symbol = " [λ](grey)"
error\_symbol = " [λ](bold red)"

[directory]
truncation\_length = 100
truncate\_to\_repo = false
style = " yellow"
format = "[:$path]($style)[$read\_only]($read\_only\_style) "

[git\_branch]
symbol = ""
style = "bold white"
format = '[\($symbol$branch\)]($style) '

[git\_status]
# I don't care about untracked files or that there's a stash present.
untracked = ""
format = '([\[$conflicted$deleted$renamed$modified$staged$behind\]]($style) )'
modified = '\*'

[status]
disabled = false
format = '[\[$status - $common\_meaning\]](green)'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is close, but is missing the current time and "uptime". And those proved to be wrinkles for me. My previous time showed the full current date &amp;amp; time along with the current timezone info (ex: &lt;code&gt;PST-0800&lt;/code&gt;). There's a &lt;code&gt;time&lt;/code&gt; module that can recreate all of this &lt;em&gt;except&lt;/em&gt; the timezone name. It should, given the docs on &lt;a href="https://docs.rs/chrono/0.4.7/chrono/format/strftime/index.html" rel="noopener noreferrer"&gt;format strings for the underlying Chrono library&lt;/a&gt;, but turns out there's a &lt;a href="https://github.com/starship/starship/discussions/2360" rel="noopener noreferrer"&gt;bug there that causes that to not work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But, Starship supports custom commands to be in a module, so I added a custom command to just defer to the standard &lt;code&gt;date&lt;/code&gt; command on *-nix type systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[custom.tztime]
command = 'date +"%a %b %d %Y %l:%M%p (%Z%z)"'
when = "true"
format = '[\[$symbol($output)\]](green)'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives me the time as it was before. The &lt;code&gt;when&lt;/code&gt; bit there is &lt;code&gt;true&lt;/code&gt; so that this command is &lt;em&gt;always&lt;/em&gt; displayed. We'll get to how to control where custom commands show up in a minute, but there was one more custom command I needed for my system uptime. The way I did this in my old prompt was a Bash function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function uptimeinfo {
    uptime | perl -ne 'if(/\d\s+up(.\*),\s+\d+\s+users/) { $s = $1; $s =~ s/^\s+|\s+$//g; print $s; }'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I used &lt;code&gt;uptimeinfo&lt;/code&gt; in my prompt. But that's Bash-specific, so instead I created a little shell script called &lt;code&gt;uptime.sh&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh

echo "[`uptime | perl -ne 'if(/\d\s+up(.\*),\s+\d+\s+users/) { $s = $1; $s =~ s/^\s+|\s+$//g; print $s; }'`]"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which just does the same thing, echoes out the system uptime with it filtered through Perl to make it more concise. Now the starship config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[custom.uptime]
command = "uptime.sh"
when = "true"
format = "[$symbol($output)](green)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now back to that layout question: we control the order of things by the global &lt;code&gt;format&lt;/code&gt; setting, but how do we refer to custom commands? Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;format = """
$status \
${custom.tztime} \
${custom.uptime} \
$username\

.... rest of the file ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ie &lt;code&gt;${custom.&amp;lt;your custom module name&amp;gt;}&lt;/code&gt;. Ok with all that, I then had everything I needed, and continued to flesh out my prompt. My final config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;format = """
$status \
${custom.tztime} \
${custom.uptime} \
$username\
$hostname\
$shlvl\
$kubernetes\
$directory\
$git\_branch\
$git\_commit\
$git\_state\
$git\_status\
$docker\_context\
$package\
$cmake\
$nodejs\
$perl\
$python \
$ruby\
$rust\
$terraform\
$vagrant\
$nix\_shell\
$conda\
$aws \
$env\_var\
$cmd\_duration\
$line\_break\
$character"""

# Don't print a new line at the start of the prompt
add\_newline = false

[aws]
format = '\[AWS: [$profile/($region)]($style)\]'
symbol = ''
style = 'bold white'

[character]
success\_symbol = " [λ](grey)"
error\_symbol = " [λ](bold red)"

[cmd\_duration]
min\_time = 1000

[directory]
truncation\_length = 100
truncate\_to\_repo = false
style = " yellow"
format = "[:$path]($style)[$read\_only]($read\_only\_style) "

[git\_branch]
symbol = ""
style = "bold white"
format = '[\($symbol$branch\)]($style) '

[git\_status]
# I don't care about untracked files or that there's a stash present.
untracked = ""
format = '([\[$conflicted$deleted$renamed$modified$staged$behind\]]($style) )'
modified = '\*'

[python]
format = '[${symbol}${pyenv\_prefix}(${version} )(\($virtualenv\))]($style)'

[status]
disabled = false
format = '[\[$status - $common\_meaning\]](green)'

[custom.tztime]
command = 'date +"%a %b %d %Y %l:%M%p (%Z%z)"'
when = "true"
format = '[\[$symbol($output)\]](green)'

[custom.uptime]
command = "uptime.sh"
when = "true"
format = "[$symbol($output)](green)"

[env\_var]
variable = "0"

#### Disabled modules ####

# add these back to format if you want them:
# $time\
# $hg\_branch\
# $dart\
# $dotnet\
# $elixir\
# $elm\
# $erlang\
# $golang\
# $helm\
# $java\
# $julia\
# $kotlin\
# $nim\
# $ocaml\
# $php\
# $purescript\
# $swift\
# $zig\
# $memory\_usage\
# $gcloud\
# $openstack\
# $crystal\
# $lua\
# $jobs\
# $battery\
[hg\_branch]
disabled = true
[dart]
disabled = true
[dotnet]
disabled = true
[elixir]
disabled = true
[elm]
disabled = true
[erlang]
disabled = true
[golang]
disabled = true
[helm]
disabled = true
[java]
disabled = true
[julia]
disabled = true
[kotlin]
disabled = true
[nim]
disabled = true
[ocaml]
disabled = true
[php]
disabled = true
[purescript]
disabled = true
[swift]
disabled = true
[zig]
disabled = true
[memory\_usage]
disabled = true
[gcloud]
disabled = true
[openstack]
disabled = true
[crystal]
disabled = true
[lua]
disabled = true
[jobs]
disabled = true
[battery]
disabled = true

# Until these get resolved, doing my own datetime with date:
# https://github.com/starship/starship/discussions/2360#discussioncomment-391911
# https://github.com/chronotope/chrono/issues/288
[time]
disabled = true
# format = '[\[$time\]](green) '
# time\_format = "%a %b %d %Y %l:%M%p (%z)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note current version (in case I revise in the future) is at: &lt;a href="https://github.com/pzelnip/dotfiles/blob/mainline/.config/starship.toml" rel="noopener noreferrer"&gt;https://github.com/pzelnip/dotfiles/blob/mainline/.config/starship.toml&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This gives a prompt like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2FnewPrompt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2FnewPrompt.png" alt="New Starship-Powered Prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty sweet, lots of dynamicism where needed, but still has all the things I liked from before. Definitely took some time to get this just the way I liked it, but am happy with the result, and as a bonus: now I have the same prompt if I'm in Bash, Zsh or whatever.&lt;/p&gt;

</description>
      <category>terminal</category>
      <category>starship</category>
    </item>
    <item>
      <title>Building A VS Code Extension Without Installing Node By Docker Magic</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sat, 27 Feb 2021 00:14:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/building-a-vs-code-extension-without-installing-node-by-docker-magic-5a82</link>
      <guid>https://dev.to/pzelnip/building-a-vs-code-extension-without-installing-node-by-docker-magic-5a82</guid>
      <description>&lt;p&gt;I recently wanted to work through the &lt;a href="https://code.visualstudio.com/api/get-started/your-first-extension"&gt;tutorial for building a VS Code extension&lt;/a&gt;, but the first step is to install &lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; locally, which, well, I don't want to do. Sorry to the Node peeps out there, but I don't want to touch the (large and rather complex) Node ecosystem just to try out building a VS Code extension. So I then thought, "Hey, you can install Node on a Linux box, so why can't I just do it inside a Docker container?"&lt;/p&gt;

&lt;p&gt;And of course, you can, and not only that, but with the magic that is the &lt;a href="https://code.visualstudio.com/docs/remote/containers"&gt;VS Code Remote Containers extension&lt;/a&gt; you can even have VS Code work as if it's all on your local host machine. Let's give this a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install The Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;First step: install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack"&gt;Remote Development&lt;/a&gt; extension pack from Microsoft. This will allow you to treat a running container, ssh connection to a remote machine, or Linux environment via WSL (assuming you are a Windows person) as though it's a local folder. If you've never played with this before, it really is worth checking out as it's amazing.&lt;/p&gt;

&lt;p&gt;I'm going to assume you also have &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; installed. If not, follow the instructions on their site, or any one of the 59 million or so &lt;a href="https://www.google.com/search?q=how+to+install+docker"&gt;tutorials online&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Dockerfile
&lt;/h2&gt;

&lt;p&gt;Ok, now let's create a &lt;code&gt;Dockerfile&lt;/code&gt; that has what we need. The VS Code tutorial mentions you need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Yeoman&lt;/li&gt;
&lt;li&gt;The VS Code Extension Generator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, all those things boil down to some basic terminal commands, but as a simple starting point, let's use the &lt;a href="https://hub.docker.com/_/node"&gt;official Node.js Docker image&lt;/a&gt; to get Node.js. To do this, make &lt;code&gt;node:10-alpine&lt;/code&gt; the base image, and then install those dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:10-alpine

RUN apk add --update git bash
RUN npm install -g yo generator-code

USER node

ENTRYPOINT /bin/bash

WORKDIR /home/node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking this down a bit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;FROM&lt;/code&gt; line says the image to use as a base image is the &lt;code&gt;node:10-alpine&lt;/code&gt; image from Dockerhub. This gives you npm &amp;amp; whatnot already installed.&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;apk add&lt;/code&gt; line installs Git and Bash (alpine doesn't have Bash installed by default)&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;npm install -g&lt;/code&gt; line installs Yeoman and the VS Code Extension Generator&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;USER&lt;/code&gt; line creates a user called &lt;code&gt;node&lt;/code&gt;, which you need as otherwise &lt;code&gt;yo&lt;/code&gt; fails when you run it in the container due to permission issues (doesn't seem to like running as root)&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;ENTRYPOINT&lt;/code&gt; says when a container is started from this image, start off by running &lt;code&gt;/bin/bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;WORKDIR&lt;/code&gt; says when a container is started from this image, start in the &lt;code&gt;/home/node&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save this file as &lt;code&gt;Dockerfile&lt;/code&gt;. Note that I used &lt;code&gt;node:10-alpine&lt;/code&gt; (so a 10.x version of Node), feel free to replace with a newer version if you want (I have no idea what version the VS Code Extension Generator wants).&lt;/p&gt;

&lt;h2&gt;
  
  
  Build The Docker Image
&lt;/h2&gt;

&lt;p&gt;Now you want to build the Docker image, run this command in the same directory as the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t vscodeenv:latest .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;vscodeenv&lt;/code&gt; name is totally arbitrary, feel free to name it whatever you want, but that's the name I'll use for this blog post. You'll see a bunch of output, and after it's done, you should be able to see the built image when you do a &lt;code&gt;docker images&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker images
REPOSITORY TAG    IMAGE ID     CREATED        SIZE
vscodeenv  latest 37d9e66fffbc 48 minutes ago 186MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run the Image
&lt;/h2&gt;

&lt;p&gt;Now we have a built Docker image with all the tooling you need for the tutorial. Next step is to spin up a container from this image. I'm sure if you get into the VS Code &lt;code&gt;devcontainer.json&lt;/code&gt; stuff you could do this from within VS Code, but I just do it from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -it --name vscodeenv -v /Users/aparkin/dockerpath/:/home/node --rm vscodeenv:latest
bash-5.0$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;/Users/aparkin/dockerpath/&lt;/code&gt; with the full path to a directory where you want to put your extensions code (it's perfectly fine to be the same directory where you put the&lt;code&gt;Dockerfile&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Note that this just drops you into a bash shell inside the running container. Leave this window open, so long as this is open your container will be running. Once you type "exit" here, the container will be terminated (which we don't want to do until we're done working on our little extension).&lt;/p&gt;

&lt;p&gt;To break the &lt;code&gt;docker run&lt;/code&gt; command down a bit, the key bits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-it&lt;/code&gt; means run in interactive mode (ie where you can type in commands)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name vscodeenv&lt;/code&gt; gives the container the name &lt;code&gt;vscodeenv&lt;/code&gt; (again, this is arbitrary)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v .....&lt;/code&gt; tells it to mount the path you specified on your local host filesystem into &lt;code&gt;/home/node&lt;/code&gt; in the running container (so any files in the path you specify will show up in the container inside &lt;code&gt;/home/node&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--rm&lt;/code&gt; tells Docker to delete the container once you exit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create The Skeleton Extension
&lt;/h2&gt;

&lt;p&gt;The VS Code tutorial indicates after installing everything, you should run the generator. Do this in the running docker container with the &lt;code&gt;yo code&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash-5.0$ yo code

     _-----_ ╭──────────────────────────╮
    | | │ Welcome to the Visual │
    |--(o)--| │ Studio Code Extension │
   `---------´ │ generator! │
    ( _´U`_ ) ╰──────────────────────────╯
    / ___A___ \ /
     | ~ |
   __'.\_\_\_.'__
 ´ ` |° ´ Y `

? What type of extension do you want to create? (Use arrow keys)
❯ New Extension (TypeScript)
  New Extension (JavaScript)
  New Color Theme
  New Language Support
  New Code Snippets
  New Keymap
  New Extension Pack
  New Language Pack (Localization)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generator walks you through creating your first extension. Following VS Code's tutorial I picked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New Extension (TypeScript)&lt;/li&gt;
&lt;li&gt;Name of my extension: "AdamsExtension"&lt;/li&gt;
&lt;li&gt;Identifier of my extension: "adamsextension" (the default)&lt;/li&gt;
&lt;li&gt;Description I entered random gibberish&lt;/li&gt;
&lt;li&gt;Initialize a Git repo: yes&lt;/li&gt;
&lt;li&gt;Bundle with Webpack: no&lt;/li&gt;
&lt;li&gt;Package manager: npm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After that, it goes ahead and installs all the various npm dependencies -- &lt;em&gt;all within the docker container&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attach To the Container
&lt;/h2&gt;

&lt;p&gt;Now in VS Code open the command palette and search for&lt;code&gt;Remote-Containers: Attach to Running Container...&lt;/code&gt;. Pick this, and then your running container called &lt;code&gt;vscodeenv&lt;/code&gt; should appear in the list:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4OAZFv3D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/runningContainer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4OAZFv3D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/runningContainer.png" alt="Attaching to the running image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pick it, and VS Code will open a new Window "attached" to the running container. For more details, consult &lt;a href="https://code.visualstudio.com/docs/remote/attach-container"&gt;the official docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now click on "Open Folder" and navigate to your &lt;code&gt;adamsextension&lt;/code&gt; (or whatever you called your extension) folder and click OK. You then get a VS Code window "attached" to the running docker container, with your test extension open and ready to play with. Here's a screenshot to give an idea:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hli8tkBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/attachedToContainer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hli8tkBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/attachedToContainer.png" alt="Extension open in VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can hit &lt;code&gt;F5&lt;/code&gt; and VS Code will open up a new Extension Development Host window with your test extension loaded. In that window you should be able to search for the "Hello World" command in the command palette and run the command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profit
&lt;/h2&gt;

&lt;p&gt;And that's it, no Node.js or any other tooling installed to your local machine other than VS Code &amp;amp; Docker. Once you're done playing around, exit out of the running Docker container (enter "exit" in the bash prompt) and the container will be terminated, and all the files you created will remain in the path you mounted into the container.&lt;/p&gt;

&lt;p&gt;If you want to later pick up where you left off, just run the same &lt;code&gt;docker run&lt;/code&gt; command as before, re-attach to the container, and re-open the folder.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>node</category>
      <category>docker</category>
    </item>
    <item>
      <title>Git Image Diff with iTerm2</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sun, 14 Feb 2021 20:37:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/git-image-diff-with-iterm2-d1g</link>
      <guid>https://dev.to/pzelnip/git-image-diff-with-iterm2-d1g</guid>
      <description>&lt;p&gt;So iTerm2 has this neat &lt;code&gt;imgcat&lt;/code&gt; command that allows you to "cat" or view an image right in the terminal. It's pretty sweet, but I had an idea: what if we used that with Git for diffing changed images in a&lt;code&gt;git diff&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;This seems like it should be possible, you can replace what git does when you do a diff of two different binary files. For example, the &lt;a href="https://zachholman.com/posts/command-line-image-diffs/" rel="noopener noreferrer"&gt;spaceman-diff package by Zach Holman&lt;/a&gt; does this to do a diff of two images as ASCII art.&lt;/p&gt;

&lt;p&gt;We want to do the same, but instead of seeing an ASCII art version of an image, it'd be cool to see the two versions of the image itself right in the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Git Attributes
&lt;/h2&gt;

&lt;p&gt;The first thing is create a file in your home directory called &lt;code&gt;~/.config/git/attributes&lt;/code&gt;. In here you can define a mapping between file types and what command Git will run when doing a diff of those filetypes. In my case I entered the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\*.png diff=image
\*.jpg diff=image
\*.gif diff=image
\*.jpeg diff=image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells git that when doing a diff of a file that ends with &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;jpg&lt;/code&gt;,&lt;code&gt;gif&lt;/code&gt;, or &lt;code&gt;jpeg&lt;/code&gt; to use the &lt;code&gt;image&lt;/code&gt; config.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up The Image Config
&lt;/h2&gt;

&lt;p&gt;Now, in your &lt;code&gt;~/.gitconfig&lt;/code&gt; file, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[diff "image"]
    textconv = imgcat

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Git to use iTerm's &lt;code&gt;imgcat&lt;/code&gt; script for converting the binary image file to text. This seems weird, but this is actually how iTerm's imgcat command works: it converts the binary image into a textual stream that iTerm knows how to understand and then render as an actual image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put imgcat In Your Path
&lt;/h2&gt;

&lt;p&gt;Next is put iTerm's &lt;code&gt;imcat&lt;/code&gt; script somewhere on your path. You can download it from &lt;a href="https://iterm2.com/utilities/imgcat" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Save that somewhere, make sure you chmod it to be executable (&lt;code&gt;chmod +x imgcat&lt;/code&gt;), and then throw it into some directory on your path. You can confirm it's there by typing &lt;code&gt;imgcat&lt;/code&gt; into an iTerm window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ imgcat
Usage: imgcat [-p] filename ...
   or: cat filename | imgcat

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you have to put this script in your path, you can't just rely on the version that's inlined into your terminal with iTerm's shell integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profit
&lt;/h2&gt;

&lt;p&gt;Now when you change an image in a Git repo and do a &lt;code&gt;git diff&lt;/code&gt; while in iTerm you'll see a preview of the original image and the changed image. Example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2FimgcatDiff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2FimgcatDiff.png" alt="Showing the image diffing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above image is the original, the 2nd image is what I changed it to. Note that because imgcat is iTerm specific, it won't work in other terminals. If you do a &lt;code&gt;git diff&lt;/code&gt; in a different terminal (ex: the integrated terminal in VS Code) you'll see just the ordinary blank output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2Fimgcatdiffvscode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2Fimgcatdiffvscode.png" alt="Image diff in non-iTerm terminal"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iterm</category>
      <category>iterm2</category>
      <category>git</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Python manage.py pytest or Making Django Use Pytest</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sun, 19 Apr 2020 01:51:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/python-manage-py-pytest-or-making-django-use-pytest-5212</link>
      <guid>https://dev.to/pzelnip/python-manage-py-pytest-or-making-django-use-pytest-5212</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally posted on Apr 18, 2020 to &lt;a href="https://www.codependentcodr.com/python-managepy-pytest-or-making-django-use-pytest.html#python-managepy-pytest-or-making-django-use-pytest"&gt;https://www.codependentcodr.com/python-managepy-pytest-or-making-django-use-pytest.html#python-managepy-pytest-or-making-django-use-pytest&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Edit: For those who prefer visual content, I've recorded this as a video on Youtube which you can find at: &lt;a href="https://youtu.be/toKW2YLnGFQ"&gt;https://youtu.be/toKW2YLnGFQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, so I've had to convert some Django projects to use Pytest as the test runner rather than the built in one that Django uses. This is actually pretty straightforward, and I even &lt;a href="https://www.youtube.com/watch?v=7it7JFPInX0"&gt;recorded a Youtube video&lt;/a&gt; showing the process.&lt;/p&gt;

&lt;p&gt;That's all fine and good, but one of the complaints I've heard from Django-ista's (is that a term? Djangoites? Django Devotees?) is that it means now the good old normal &lt;code&gt;python manage.py test&lt;/code&gt; no longer works.&lt;/p&gt;

&lt;p&gt;So challenge accepted, as one can certainly create &lt;a href="https://docs.djangoproject.com/en/2.2/howto/custom-management-commands/"&gt;custom manage.py commands&lt;/a&gt; in Django.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python Manage.py pytest
&lt;/h2&gt;

&lt;p&gt;So first challenge is "how do we run pytest from Python?" as normally you run Pytest as a command line tool. As it turns out there's &lt;a href="https://docs.pytest.org/en/latest/usage.html#calling-pytest-from-python-code"&gt;docs on how to do this on Pytest's site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The trick is to &lt;code&gt;import pytest&lt;/code&gt; and then call &lt;code&gt;pytest.main()&lt;/code&gt; passing in the same command line arguments you'd give to Pytest in the terminal to thatfunction. As an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytest.main(['--lf'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would be the same thing as doing &lt;code&gt;pytest --lf&lt;/code&gt; on the command-line. Easy peasy.So started a basic custom management command in a file called &lt;code&gt;pytest.py&lt;/code&gt; and put it into my Django project's &lt;code&gt;management/commands&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pytest
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "Runs tests with Pytest"

    def handle(self, \*args, \*\*options):
        pytest.main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, in that now I can do &lt;code&gt;python manage.py pytest&lt;/code&gt; and it'll run Pytest as if I just ran the &lt;code&gt;pytest&lt;/code&gt; executable in the current directory.&lt;/p&gt;

&lt;p&gt;Cool, but how do I start passing arguments? Normally in a custom Django management command you define a &lt;code&gt;add_arguments&lt;/code&gt; function and use the &lt;code&gt;argparse&lt;/code&gt;module to define the expected arguments for your custom command. In this case though, I essentially want the interface to Pytest, which would be non-trivial to recreate by hand (there's a lot of options on that Pytest executable).&lt;/p&gt;

&lt;p&gt;But, with argparse, there is a way to essentially say "accept any arguments",and that's the &lt;a href="https://docs.python.org/3/library/argparse.html#nargs"&gt;&lt;code&gt;argparse.REMAINDER&lt;/code&gt; value for the &lt;code&gt;nargs&lt;/code&gt;parameter&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;argparse.REMAINDER&lt;/code&gt;. All the remaining command-line arguments are gathered into a list. This is commonly useful for command line utilities that dispatch to other command line utilities&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perfect, that's exactly what I want. Adding to our management command is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import argparse
import pytest
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "Runs tests with Pytest"

    def add\_arguments(self, parser):
        parser.add\_argument("args", nargs=argparse.REMAINDER)

    def handle(self, \*args, \*\*options):
        pytest.main(list(args))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now whatever arguments we pass to our &lt;code&gt;manage.py pytest&lt;/code&gt; command will be passed directly through to the &lt;code&gt;pytest.main&lt;/code&gt; function. Exactly what I want, and super concise.&lt;/p&gt;

&lt;h2&gt;
  
  
  But That's Still Different
&lt;/h2&gt;

&lt;p&gt;At this point it worked, but I could still hear those nagging voices saying,"yeah but &lt;code&gt;manage.py pytest&lt;/code&gt; is not the same as &lt;code&gt;manage.py test&lt;/code&gt;". Fine, as it turns out though you can override any of the built-in manage.py commands.&lt;/p&gt;

&lt;p&gt;The trick is to just create a custom management command with the name of the command you want to override, and make sure your app is the last one in Django's&lt;code&gt;INSTALLED_APPS&lt;/code&gt; setting. So in our case we could just rename our&lt;code&gt;management/pytest.py&lt;/code&gt; file to &lt;code&gt;management/test.py&lt;/code&gt; and it'd work. But I kindaliked having both (ie both &lt;code&gt;manage.py pytest&lt;/code&gt; and &lt;code&gt;manage.py test&lt;/code&gt; being effectively an alias to it). So I created a &lt;code&gt;management/test.py&lt;/code&gt; file and put in the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Override the built in Django manage.py test with pytest
from djangotest.management.commands.pytest import Command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yup, that's it, 1 line of code. Now doing a &lt;code&gt;python manage.py test&lt;/code&gt; runs Pytest as the test runner.&lt;/p&gt;

&lt;p&gt;Did this all on a test project, source is up on Github at:&lt;a href="https://github.com/pzelnip/djangotest"&gt;https://github.com/pzelnip/djangotest&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Or if you just want to see the management commands, they're at:&lt;a href="https://github.com/pzelnip/djangotest/tree/master/djangotest/management/commands"&gt;https://github.com/pzelnip/djangotest/tree/master/djangotest/management/commands&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>pytest</category>
      <category>unittest</category>
    </item>
    <item>
      <title>Visual Studio Code Tasks and Split Terminals</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Tue, 19 Feb 2019 16:43:03 +0000</pubDate>
      <link>https://dev.to/pzelnip/visual-studio-code-tasks-and-split-terminals-2ghk</link>
      <guid>https://dev.to/pzelnip/visual-studio-code-tasks-and-split-terminals-2ghk</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally posted on Feb 10, 2019 to &lt;a href="https://www.codependentcodr.com/visual-studio-code-tasks-and-split-terminals.html#visual-studio-code-tasks-and-split-terminals" rel="noopener noreferrer"&gt;https://www.codependentcodr.com/visual-studio-code-tasks-and-split-terminals.html#visual-studio-code-tasks-and-split-terminals&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So as a big &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; fan, I've long made use of the &lt;a href="https://code.visualstudio.com/docs/editor/tasks" rel="noopener noreferrer"&gt;tasks feature&lt;/a&gt;.  The most recent (January 2019, 1.31) update added a cool new feature related to this that I've been waiting for for some time.  I thought I'd do a little write up about this and how I use tasks with VS Code, particularly as a Pythonista.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task Basics
&lt;/h2&gt;

&lt;p&gt;As a starting point, to give a basic idea of what tasks are, they're effectively little shortcuts to terminal commands that you can trigger from within VS Code.  They're commonly used for things like triggering build tasks, or starting up a local dev server, etc.  The thing that makes them nice is that they can be triggered from the command pallette much like normal VS Code commands.  For example, when working on this blog, I'll use a task to fire up a local dev server to test out content before committing/pushing it.  It looks something like this:&lt;/p&gt;


  
  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codependentcodr.com%2Fstatic%2Fimgs%2Fvscodetask.gif"&gt;


&lt;p&gt;At this point I can then go to &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt; and see the content I've been working on. Handy.  To create a task, you open up the command pallette and pick "Tasks: Configure Task" and you'll be prompted with some default template tasks, or the option to "Create tasks.json file from template" which gives you total control and is the option I use.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;tasks.json&lt;/code&gt; file contains a number of JSON blobs which define your tasks.  They look something like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// See https://go.microsoft.com/fwlink/?LinkId=733558&lt;/span&gt;
    &lt;span class="c1"&gt;// for the documentation about the tasks.json format&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tasks&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source $(dirname ${config:python.pythonPath})/activate &amp;amp;&amp;amp; make devserver&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is the definition for my run local dev server that I showed in the video.  You can have as many tasks as you want, the &lt;code&gt;tasks&lt;/code&gt; property is just a list of these definitions.  The full list of properties and options are&lt;br&gt;
&lt;a href="https://code.visualstudio.com/docs/editor/tasks#_custom-tasks" rel="noopener noreferrer"&gt;in Microsoft's excellent docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Python Tasks
&lt;/h2&gt;

&lt;p&gt;So now that we have an idea of what tasks are, what are some of the neat things you can do with them, particularly from the perspective of a Python developer?  These are some of the common ones I set up, most of which are &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; related, since much of my day job is working in that framework:&lt;/p&gt;

&lt;h3&gt;
  
  
  Running a Dev Server
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${config:python.pythonPath} manage.py runserver --noreload&lt;/span&gt;&lt;span class="dl"&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 is basically the analogy to the task I showed previously, just using Django's &lt;code&gt;runserver&lt;/code&gt; command.  One thing to note about this: note that the label is the same as the one for my blog project.  &lt;code&gt;tasks.json&lt;/code&gt; files are stored per-project, but one neat thing is you can assign a hotkey to a given task.  In my &lt;code&gt;keybindings.json&lt;/code&gt; I&lt;br&gt;
have:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cmd+shift+r&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workbench.action.tasks.runTask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;args&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Server&lt;/span&gt;&lt;span class="dl"&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 allows me to start a local dev server by simply hitting a hotkey, and so long as I name that "start up a local dev environment" task the same on each project, it's the same keystroke to start up a local dev environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hitting a Health Check URL
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Healthcheck (requires running server)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;curl http://127.0.0.1:6100/health&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once I have a local server running, it's handy to be able to quickly hit the&lt;br&gt;
&lt;a href="https://microservices.io/patterns/observability/health-check-api.html" rel="noopener noreferrer"&gt;health check&lt;/a&gt; url (you added a health check to your API right?).  Again, this is small, but handy as it saves me the trouble of tabbing over to a terminal window, typing out the &lt;code&gt;curl&lt;/code&gt; command, realizing that this project runs on a different port, going to look that up, etc, etc, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Unit Tests
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Unit Tests&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kind&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isDefault&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${config:python.pythonPath} -m pytest -rxXs --ds=projectname.settings.local_test --random-order&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Whenever possible I use &lt;a href="https://pytest.org/" rel="noopener noreferrer"&gt;pytest&lt;/a&gt; for running my unit tests.  Normally this is run from the command line as something like &lt;code&gt;pytest &amp;lt;name of directory containing tests&amp;gt;&lt;/code&gt;.  The problem though is that pytest gets installed to a virtual environment, so how do I give the full path to the virtual env without making the task machine specific?  The answer is I run it as a module and just use the &lt;code&gt;config:python.pythonPath&lt;/code&gt; variable to reference whatever the current Python environment is.  The other options are some common ones I feed to pytest, ex the &lt;code&gt;--ds&lt;/code&gt; switch is for specifying the &lt;code&gt;DJANGO_SETTINGS_MODULE&lt;/code&gt; environment variable.  &lt;code&gt;--random-order&lt;/code&gt; uses the &lt;a href="https://pypi.org/project/pytest-random-order/" rel="noopener noreferrer"&gt;Pytest Random Order plugin&lt;/a&gt; to run the tests in a random order on each test run (which has discovered bugs in my code/tests).&lt;/p&gt;

&lt;p&gt;I also set a hotkey for this task:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shift+cmd+f11&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workbench.action.tasks.test&lt;/span&gt;&lt;span class="dl"&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 makes use of the &lt;code&gt;kind&lt;/code&gt; property of the task definition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Dependencies
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Update Python Dependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${config:python.pythonPath} -m pip install -r requirements.txt --upgrade &amp;amp;&amp;amp; ${config:python.pythonPath} -m pip install -r requirements-dev.txt --upgrade&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I still use &lt;code&gt;requirements.txt&lt;/code&gt; files (I really should spend the time to learn &lt;a href="https://github.com/pypa/pipenv" rel="noopener noreferrer"&gt;pipenv&lt;/a&gt;, but alas).  With this task I can quickly update all my project's dependencies.  I also separate out my project's dependencies and my project's dev dependencies (think things like pytest or pylint) into separate files.  The reason for this is that I can then let my dev dependencies "float", and most projects I work on also build a Docker image at the end of the day, so separating the dependencies allows me to only install the dependencies needed for running the project into the Docker image, which cuts down on image size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Many Many More
&lt;/h3&gt;

&lt;p&gt;This is just scratching the surface, any time I find myself commonly running commands in a terminal window on a project, I'll spend the minute or so to turn that into a VS Code task.&lt;/p&gt;

&lt;p&gt;Lastly, one of the key points here is that I do essentially these same tasks on any project I work on and I just tweak the specific commands for the particular project.  This creates a common/familiar workflow for me regardless of if it's a Django project, Flask, or even an entirely different tech (I had a Java project with a REST API and I created many of the same tasks for that).&lt;/p&gt;

&lt;h2&gt;
  
  
  New Tricks
&lt;/h2&gt;

&lt;p&gt;As mentioned, in the January 2019 update they added a new feature related to tasks that I'm a huge fan of: &lt;a href="https://code.visualstudio.com/updates/v1_31#_task-output-support-split-terminals" rel="noopener noreferrer"&gt;Task Output Split Terminals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows you to have a task spawned into a split terminal window to another (already running) task.  This is really handy when you have say a task for running the dev server and another task for say tailing the log file of that server as you can have them appear side-by-side in the integrated terminal.&lt;/p&gt;

&lt;p&gt;This was particularly useful for a project I work on at work where I have a Django-based server, which speaks to another local dev server via a socket connection.  Previously I had tasks set up for both of these, and I'd have to fire up each one individually, and switch between multiple terminal windows to see the output of each.  Now I can have them show up side by side in one view.  The way this works is by sharing the same &lt;code&gt;group&lt;/code&gt; property in the task's &lt;code&gt;presentation&lt;/code&gt; property:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${config:python.pythonPath} manage.py runserver --noreload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presentation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupServerStuff&lt;/span&gt;&lt;span class="dl"&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;All tasks with the same group will open up as another split terminal pane in the same terminal window.  Very nice.&lt;/p&gt;

&lt;p&gt;This got me to thinking though: rather than start each task individually, is there a way to have tasks "call" or "spawn" other tasks?  And as it turns out there is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependsOn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run TCP Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Django Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tail Log File&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run Django Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${config:python.pythonPath} manage.py runserver --noreload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presentation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupServerStuff&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run TCP Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${config:python.pythonPath} scripts/tcp_server.py&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presentation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupServerStuff&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tail Log File&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tail -f /tmp/logfile.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presentation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupServerStuff&lt;/span&gt;&lt;span class="dl"&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;Check out that &lt;code&gt;Run Server&lt;/code&gt; task -- it spawns three other tasks I have defined: "Run Django Server" (which was my previous "Run Server" task), "Run TCP Server" (the simulated socket server), and "Tail Log File" which just tails the logfile that Django is logging to.&lt;/p&gt;

&lt;p&gt;And of course, because it's called &lt;code&gt;Run Server&lt;/code&gt; the same hotkey I defined previously will spawn up a new terminal window split 3-ways with these tasks running.  All with a single keystroke.  That's pretty powerful stuff!&lt;/p&gt;

&lt;p&gt;In any case, I hope this was a useful overview of Tasks in VS Code.  Have you come up with any creative uses for them?  Lemme know in the comments!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>python</category>
      <category>django</category>
    </item>
    <item>
      <title>Disabling Pylint Messages</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sun, 12 Aug 2018 22:20:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/disabling-pylint-messages-4i08</link>
      <guid>https://dev.to/pzelnip/disabling-pylint-messages-4i08</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally posted on Aug 12, 2018 to &lt;a href="https://www.codependentcodr.com/disabling-pylint-messages.html#disabling-pylint-messages" rel="noopener noreferrer"&gt;https://www.codependentcodr.com/disabling-pylint-messages.html#disabling-pylint-messages&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Small tip of the day since I keep having to look this up.  If you use&lt;br&gt;
&lt;a href="https://pylint.org/" rel="noopener noreferrer"&gt;Pylint&lt;/a&gt; for static analysis of your code, you might find that you'll want to disable a particular rule for a particular line or file. That is, you don't want to permanently disable this rule, but just for this one special spot where the rule doesn't make sense.&lt;/p&gt;

&lt;p&gt;I find this particularly common in unit test code as my test method names tend to be long, violating rule C0103 "Name doesn't conform to naming rules".  For example, a test name might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_message_builder_generates_correct_bytestring_when_no_argument_supplied&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is very self-descriptive, and when that test fails, it makes it much&lt;br&gt;
easier to figure out what might've gone wrong.  The problem is that Pylint will flag that line since it's greater than 30 characters long, violating the style&lt;br&gt;
guidelines.  We could disable this rule across the entire codebase, but outside&lt;br&gt;
of tests, the rule makes sense.&lt;/p&gt;

&lt;p&gt;This is where local disables come in, which take the form of comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# pylint disable=C0103
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_message_builder_generates_correct_bytestring_when_no_argument_supplied&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will suppress code C0103 for the remainder of the scope (module or block), or until it's re-enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# pylint disable=C0103
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_message_builder_generates_correct_bytestring_when_no_argument_supplied&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;


&lt;span class="c1"&gt;# still disabled here...
&lt;/span&gt;
&lt;span class="c1"&gt;# pylint enable=C0103
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;but_not_disabled_here_so_this_name_will_get_flagged_by_pylint&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also (and it's generally better practice) use the "verbose name" for a&lt;br&gt;
particular code rather than the shorthand code.  For example, this is&lt;br&gt;
equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# pylint disable=invalid-name
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_message_builder_generates_correct_bytestring_when_no_argument_supplied&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A question then becomes  "how do I know what the verbose name for a code is?"  And the answer is to use the &lt;code&gt;--list-msgs&lt;/code&gt; argument to Pylint on the command line, and it'll spit them all out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pylint &lt;span class="nt"&gt;--list-msgs&lt;/span&gt;
:blacklisted-name &lt;span class="o"&gt;(&lt;/span&gt;C0102&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="k"&gt;*&lt;/span&gt;Black listed name &lt;span class="s2"&gt;"%s"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;
  Used when the name is listed &lt;span class="k"&gt;in &lt;/span&gt;the black list &lt;span class="o"&gt;(&lt;/span&gt;unauthorized names&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
:invalid-name &lt;span class="o"&gt;(&lt;/span&gt;C0103&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="k"&gt;*&lt;/span&gt;%s name &lt;span class="s2"&gt;"%s"&lt;/span&gt; doesn&lt;span class="s1"&gt;'t conform to %s*
  Used when the name doesn'&lt;/span&gt;t conform to naming rules associated to its &lt;span class="nb"&gt;type&lt;/span&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;constant, variable, class...&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
:missing-docstring &lt;span class="o"&gt;(&lt;/span&gt;C0111&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="k"&gt;*&lt;/span&gt;Missing %s docstring&lt;span class="k"&gt;*&lt;/span&gt;
  Used when a module, &lt;span class="k"&gt;function&lt;/span&gt;, class or method has no docstring.Some special
  methods like __init__ doesn&lt;span class="s1"&gt;'t necessary require a docstring.
:empty-docstring (C0112): *Empty %s docstring*
  Used when a module, function, class or method has an empty docstring (it would
  be too easy ;).

... more lines ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One extra tip about Pylint: if you use Visual Studio Code you can turn on Pylint as your linter, and warnings will get put into the problems view.  To turn it on set the following in your VS Code settings (assuming you've installed the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python" rel="noopener noreferrer"&gt;Python extension from Microsoft&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that these are both on by default (at least in the current version).  Once you save a Python file, Pylint warnings will show up in the problems view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftzg223tx4mtnn01m977.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftzg223tx4mtnn01m977.png" alt="Pylint Warnings in VS Code Problems View" width="800" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that that screenshot also illustrates how you can filter the problems view down to show just Pylint warnings by entering "pylint" into the search box.  Also note that clicking any of those will open up that file in VS Code and navigate to the line in question.  Really handy.&lt;/p&gt;

</description>
      <category>python</category>
      <category>pylint</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Docker and Image Sizes</title>
      <dc:creator>Adam Parkin</dc:creator>
      <pubDate>Sun, 12 Aug 2018 02:30:00 +0000</pubDate>
      <link>https://dev.to/pzelnip/docker-and-image-sizes-209a</link>
      <guid>https://dev.to/pzelnip/docker-and-image-sizes-209a</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally posted at: &lt;a href="https://www.codependentcodr.com/docker-and-image-sizes.html"&gt;https://www.codependentcodr.com/docker-and-image-sizes.html&lt;/a&gt; on Aug 12, 2018&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So this surprised me. I had a Dockerfile that looked basically like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM alpine:latest

RUN apk add --no-cache --update \
    python3 nodejs-current-npm make git curl

RUN python3 -m ensurepip
RUN pip3 install --upgrade pip

RUN npm install -g markdownlint-cli

# needed for one of the packages in requirements.txt
RUN apk add --no-cache --update python3-dev gcc build-base

COPY requirements.txt /build/requirements.txt
RUN pip3 install -r /build/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Building this image resulted in an image that &lt;code&gt;docker images&lt;/code&gt; reported as 379MB insize. That's a little large so I wanted to trim.&lt;/p&gt;

&lt;p&gt;Since those packages installed just before copying&lt;code&gt;requirements.txt&lt;/code&gt; to the image were only there to be able to &lt;em&gt;install&lt;/em&gt; a package, there's no reason for them to remain in the image. Cool, so we can turf them to save on image size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM alpine:latest

RUN apk add --no-cache --update \
    python3 nodejs-current-npm make git curl

RUN python3 -m ensurepip
RUN pip3 install --upgrade pip

RUN npm install -g markdownlint-cli

# needed for one of the packages in requirements.txt
RUN apk add --no-cache --update python3-dev gcc build-base

COPY requirements.txt /build/requirements.txt
RUN pip3 install -r /build/requirements.txt

# cleanup unneeded dependencies
RUN apk del python3-dev gcc build-base
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet, and this resulted in an image size of &lt;em&gt;381MB&lt;/em&gt;, a savings of &lt;em&gt;NEGATIVE 2MB&lt;/em&gt;. Wait.... WAT?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--13Cak3dZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/wat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--13Cak3dZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.codependentcodr.com/static/imgs/wat.jpg" alt="&amp;lt;INSERT ALT TEXT HERE&amp;gt;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I &lt;em&gt;removed&lt;/em&gt; some stuff and ended up with an image that's a few MB's &lt;em&gt;larger&lt;/em&gt;? How does that work?&lt;/p&gt;

&lt;p&gt;And this is where if we want to get technical, we start talking about how Docker uses a layered filesystem and as such (not entirely unlike Git repos) once something is added to an image, it can't really (or at least easily) be removed.&lt;/p&gt;

&lt;p&gt;See this issue which mentions what I'm talking about: &lt;a href="https://github.com/gliderlabs/docker-alpine/issues/45"&gt;https://github.com/gliderlabs/docker-alpine/issues/45&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what do we do? Well, we &lt;em&gt;combine&lt;/em&gt; the operations into a single Docker instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM alpine:latest

RUN apk add --no-cache --update \
    python3 nodejs-current-npm make git curl

RUN python3 -m ensurepip
RUN pip3 install --upgrade pip

RUN npm install -g markdownlint-cli

COPY requirements.txt /build/requirements.txt

RUN apk add --no-cache --update python3-dev gcc build-base &amp;amp;&amp;amp; \
    pip3 install -r /build/requirements.txt &amp;amp;&amp;amp; \
    apk del python3-dev gcc build-base
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the add &amp;amp; the removal of the apk packages are a single Docker instruction they don't inflate the size of the built image (you can think of layers as being "checkpoints" after each instruction in a &lt;code&gt;Dockerfile&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;With this change my image size dropped &lt;em&gt;significantly&lt;/em&gt;. How much? Let's let the tool tell us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker images
REPOSITORY                   TAG                 IMAGE ID            CREATED              SIZE
someimage                    combineops          1744771da3fa        About a minute ago   216MB
someimage                    removepckgs         fc5877e2afad        4 minutes ago        381MB
someimage                    original            b6e5e43b22e0        5 minutes ago        379MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is, it dropped from &lt;em&gt;379MB&lt;/em&gt; to &lt;em&gt;216MB&lt;/em&gt;. Not a bad savings at all.&lt;/p&gt;

&lt;p&gt;This is a classic "time vs space" tradeoff though. Because I had to move the &lt;code&gt;requirements.txt&lt;/code&gt; line up, that means that builds of this image are often slower (because of the way the Docker cache works, if I change the requirements.txt file then it'll have to install those apk packages any time &lt;code&gt;requirements.txt&lt;/code&gt; changes). However, I think the savings in space (40%+) is worth it.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
