<?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: Demian Brecht</title>
    <description>The latest articles on DEV Community by Demian Brecht (@demianbrecht).</description>
    <link>https://dev.to/demianbrecht</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%2F343552%2Fae8f86ce-1ff0-4694-894d-c46048889996.jpeg</url>
      <title>DEV Community: Demian Brecht</title>
      <link>https://dev.to/demianbrecht</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/demianbrecht"/>
    <language>en</language>
    <item>
      <title>Automating Android Burp API Testing (Because Rebuilding This Sucks)</title>
      <dc:creator>Demian Brecht</dc:creator>
      <pubDate>Fri, 06 Feb 2026 01:41:42 +0000</pubDate>
      <link>https://dev.to/demianbrecht/automating-android-burp-api-testing-because-rebuilding-this-sucks-1bm0</link>
      <guid>https://dev.to/demianbrecht/automating-android-burp-api-testing-because-rebuilding-this-sucks-1bm0</guid>
      <description>&lt;p&gt;Reproducing mobile security findings is one of those tasks that sounds trivial until you actually try to do it.&lt;/p&gt;

&lt;p&gt;Desktop traffic? Easy.&lt;/p&gt;

&lt;p&gt;Android → API → Burp? Suddenly you’re juggling emulators, system certificates, proxy configuration, and half-remembered setup steps you only touch a few times a year.&lt;/p&gt;

&lt;p&gt;If you’re primarily a backend engineer, this usually means losing time just getting to the point where you can see the traffic, let alone reason about the bug.&lt;/p&gt;

&lt;p&gt;I got tired of rebuilding the same fragile setup, so I automated it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a security researcher reports an issue involving a mobile client, validating it often requires recreating an environment you don’t normally live in.&lt;/p&gt;

&lt;p&gt;On paper, the setup is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android emulator&lt;/li&gt;
&lt;li&gt;Burp Suite acting as a man-in-the-middle&lt;/li&gt;
&lt;li&gt;Burp CA installed into the system trust store&lt;/li&gt;
&lt;li&gt;Emulator traffic routed through Burp&lt;/li&gt;
&lt;li&gt;Requests flowing into your API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Emulator images behave differently&lt;/li&gt;
&lt;li&gt;Cert installation is easy to get wrong&lt;/li&gt;
&lt;li&gt;HTTPS breaks silently&lt;/li&gt;
&lt;li&gt;You end up debugging the tooling instead of the issue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is hard. It’s just annoying, fragile, and repetitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wanted something that would:&lt;/p&gt;

&lt;p&gt;Get me from “report received” to “inspectable HTTPS traffic” quickly&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require minimal Android knowledge&lt;/li&gt;
&lt;li&gt;Be repeatable and disposable&lt;/li&gt;
&lt;li&gt;Let me focus on validating the issue, not the plumbing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it. No grand framework. Just removing friction from a recurring workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What android-burp does&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;android-burp automates standing up an Android emulator wired through Burp Suite into your API.&lt;/p&gt;

&lt;p&gt;Specifically, it handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provisioning an Android emulator&lt;/li&gt;
&lt;li&gt;Installing Burp’s CA certificate into the system trust store&lt;/li&gt;
&lt;li&gt;Routing emulator traffic through Burp&lt;/li&gt;
&lt;li&gt;Getting you to inspectable HTTPS traffic with minimal ceremony&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The intent is not to replace understanding how this works — it’s to avoid re-learning it every single time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on AI assistance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m a backend engineer by trade, not a mobile specialist.&lt;/p&gt;

&lt;p&gt;This project was built with heavy AI assistance while working through unfamiliar Android internals. Not as a replacement for understanding, but as a force multiplier for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exploring tooling quickly&lt;/li&gt;
&lt;li&gt;Validating assumptions&lt;/li&gt;
&lt;li&gt;Iterating without spending days in docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is something that’s already paid for itself in saved time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who this is for&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This will probably be useful if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Work on APIs that have mobile clients&lt;/li&gt;
&lt;li&gt;Validate security reports involving Android apps&lt;/li&gt;
&lt;li&gt;Don’t want to become an Android expert just to reproduce an issue&lt;/li&gt;
&lt;li&gt;Are tired of rebuilding the same setup from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you live in mobile tooling every day, this might feel boring.&lt;/p&gt;

&lt;p&gt;If you don’t — it’s a relief.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The repo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project is fully open source here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/salesforce-misc/android-burp" rel="noopener noreferrer"&gt;https://github.com/salesforce-misc/android-burp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback, issues, and PRs are welcome. If it saves someone else from the same yak-shaving, it’s done its job.&lt;/p&gt;

</description>
      <category>android</category>
      <category>security</category>
      <category>api</category>
      <category>automation</category>
    </item>
    <item>
      <title>Gen AI: Feeding the dormant hacker</title>
      <dc:creator>Demian Brecht</dc:creator>
      <pubDate>Tue, 27 May 2025 23:16:15 +0000</pubDate>
      <link>https://dev.to/demianbrecht/gen-ai-feeding-the-dormant-hacker-1dkc</link>
      <guid>https://dev.to/demianbrecht/gen-ai-feeding-the-dormant-hacker-1dkc</guid>
      <description>&lt;p&gt;I can be a bit of a tech curmudgeon at times. I once even went as far as to write that you would &lt;a href="https://dev.to/demianbrecht/you-will-pry-vim-from-my-cold-dead-hands-29d6"&gt;pry vim from my cold, dead hands&lt;/a&gt;. I have a set of tools and processes that I’ve built up over the years that I’m comfortable with. Because of the comfort level, I’ve also traditionally been my most productive with them. In over 20 years of professional software development, I’ve never come across productivity enhancement that has gotten me to throw everything out of the window and start anew. The use of Cursor and Gen AI has done just that. Here’s the story of my experience thus far, which I’m sharing in hopes of turning my level of stoke contagious with a wider audience than my immediate circle.&lt;/p&gt;

&lt;p&gt;I'm an 80s kid. A xennial. My father was a musician, a one-man band who was a very early adopter of MIDI. I grew up around computers. I fondly remember the days of the Vic 20, Commodore 64 and the Amiga and being wide-eyed and full of wonder at the pixellated images that took what seemed forever to load. I finally got some bigger-boy shoes in the late 90s with the home PC of my dreams running Windows ‘95. Much time was spent trying to establish dial-up connections when someone was already on the line elsewhere in the house. Gaming was my main interest (from Zork to Wolfenstein 3D), but getting computers to do things was also high on my list of interests, mostly tinkering first with Logo and eventually getting into BASIC.&lt;/p&gt;

&lt;p&gt;Over the following years, I toyed with web development, hosting my very first Diablo fan site on Geocities. It wasn’t until I saw someone yell “&lt;a href="https://youtu.be/5y_SbnPx_cE?t=36" rel="noopener noreferrer"&gt;Hack the Planet!&lt;/a&gt;” (&lt;em&gt;groan&lt;/em&gt;, I know), found a local &lt;a href="https://www.2600.com/" rel="noopener noreferrer"&gt;2600 crew&lt;/a&gt; and did a little dumpster-diving when I got a little more serious about programming. I found the internet fascinating. I found the weak points fun.&lt;/p&gt;

&lt;p&gt;I started my professional career out at a small local business automation shop. I got my hands dirty with a number of different aspects of the business: Software (good ol' VB6 with an MS Access database), wiring up customer sites with cat5 cable for card scanners and providing training. I'd gotten a foot in the door. This was an exciting time for me. I was doing a lot of grunt work, but was still able to code every now and then and I actually got paid for it. I was also still spending time supplementing the more mundane aspects of my job with the hackers and the phone phreaks.&lt;/p&gt;

&lt;p&gt;Then the excitement increased by an order of magnitude when I was lucky enough (after a multitude of attempts) to get a foot in the door at Electronic Arts. At the time, I was a gamer and I finally was going to get the chance to work myself into a position where I could contribute to the games I played. By the time I’d left EA, I’d worked on a number of AAA titles, got to do some prototype work for the Wii that ended up being the basis of one of the Playground mini-games and even got to do some voice work for an NCAA Football title. During that time, my tinkering time went from network security to the open source community, particularly Python’s. While continuing my career between Popcap Games, Demonware (Activision) and then finally landing at Salesforce, I contributed to the Python Software Foundation (PSF). I contributed here and there to some of the issues in the tracker and to the http library. I even released a couple of (really unused) open source libraries.&lt;/p&gt;

&lt;p&gt;This is a synopsis intended to highlight how much I actually love what I do, not to be an autobiography. This isn’t just a job for me, it’s something I look forward to every day. Solving tough problems with computers is something I genuinely enjoy.&lt;/p&gt;

&lt;p&gt;However, along the way I lost a large portion of the drive to tinker and hack on things.&lt;/p&gt;

&lt;p&gt;Maybe it’s because I picked up too many outdoor activities. Maybe it’s because my software-related hobbies started to blend too much with my professional work. Maybe it’s because adulting cut into personal time and felt like I no longer had the chance to accomplish my lofty technical goals. Maybe it was a combination of all the above. At the end of the day, it boiled down to just not being able to find the time to achieve what I wanted to on my personal time.&lt;/p&gt;

&lt;p&gt;Then Gen AI came along.&lt;/p&gt;

&lt;p&gt;At first it seemed like a passing fad. The failure rate was high. Hallucinations were real. It seemed like it could maybe be useful for helping me to throw together some shell scripts or other mundane routine things but it wasn't likely going to be of any real use as I had to spend just as long fixing the output as I would have writing it in the first place.&lt;/p&gt;

&lt;p&gt;Then it got better. Companies started to introduce productivity initiatives which became a forcing factor for me to really give it a shot and find out where the ceiling was. I found I wasn't getting the opportunity to muck with it to the extent that I wanted to during the work day, so I started using it after work hours and over the weekends after the kids went to bed. What did I find?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This.&lt;br&gt;
Shit.&lt;br&gt;
Is.&lt;br&gt;
Fun.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"&lt;a href="https://en.wikipedia.org/wiki/Vibe_coding" rel="noopener noreferrer"&gt;Vibe coding&lt;/a&gt;"? Not quite. I’ve tried multiple paths to get me to this panacea of AI-assisted software development but never got close with anything outside of the simplest of use cases. I found that the most effective approach was to carve off a small, very specific piece of functionality, be extremely clear about the expected inputs and outputs, sometimes writing it down in some documentation and feeding that as context and instructions to the LLM. Then, begin the back and forth with the LLM. Having kids, especially teenagers, had prepared me for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask it to do something. Aim low, shoot high or in other words hope for the best but expect something half-assed.&lt;/li&gt;
&lt;li&gt;Suggest it wasn't done optimally and suggest an alternate approach.&lt;/li&gt;
&lt;li&gt;Watch it correct itself and get it a little closer.&lt;/li&gt;
&lt;li&gt;Have some back and forth while whittling away at the cruft to get where you want it to be (test coverage, etc).&lt;/li&gt;
&lt;li&gt;Expect it to do something underhanded: "&lt;em&gt;No, I really do want this to use a Red Hat base image. Please don't change it to Debian... Again&lt;/em&gt;".&lt;/li&gt;
&lt;li&gt;Expect it to not understand some things: "&lt;em&gt;Yes, I actually do want to keep the async request handlers but keep the database transactions synchronous&lt;/em&gt;".&lt;/li&gt;
&lt;li&gt;Expect to be caught in the wrong at times, admitting to yourself you were being dumb and that you can learn a thing or two from the LLM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;But&lt;/em&gt; expect that after a healthy amount of back and forth and, similar to working with your teenager, learning how to most &lt;strong&gt;&lt;em&gt;effectively communicate with them&lt;/em&gt;&lt;/strong&gt;, you will eventually get to your destination in a far shorter amount of time than it would have taken you on your own.&lt;/p&gt;

&lt;p&gt;The aspects that have been the most fun for me so far?&lt;/p&gt;

&lt;p&gt;I've gotten to play with tools and libraries that I would have likely otherwise skipped over because they're not part of my daily work (uv - yes I know, you don't need to tell me how good it is, alembic, sqlalchemy core to name a few).&lt;/p&gt;

&lt;p&gt;I’ve learned some tips and tricks that I can use in my daily work (i.e. using pg_isready as the health check in a docker-compose file like demonstrated &lt;a href="https://github.com/peter-evans/docker-compose-healthcheck/blob/master/docker-compose.yml" rel="noopener noreferrer"&gt;here&lt;/a&gt; instead of a hacky shell script).&lt;/p&gt;

&lt;p&gt;A healthy amount of code can be written in a relatively short amount of time, though you do have to keep an eye on general correctness and needless complexity. My first real attempt at a putting something together outside of one-off scripts or Splunk queries ended up as a FastAPI project that had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dockerfile&lt;/li&gt;
&lt;li&gt;docker-compose config for local dev with postgres tie-in and sane dependencies&lt;/li&gt;
&lt;li&gt;CRUD endpoints for users and related models with unit tests&lt;/li&gt;
&lt;li&gt;E-mail verification for new user accounts with modular providers&lt;/li&gt;
&lt;li&gt;Migrations courtesy of alembic (properly configured)&lt;/li&gt;
&lt;li&gt;Beginnings of authentication endpoints with OAuth providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;In 2 hours!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's probably a stretch to think I could have accomplished that with the tools and libraries that I use on a daily basis. There's no chance I would have come close with frameworks that I've never really used.&lt;/p&gt;

&lt;p&gt;If you’ve found yourself being an AI curmudgeon, or are only using it because you’ve been told that you have to, I challenge you to play around with it more. A lot more. Explore its capabilities. See where it fails. See where it works. See not only what it can do for you but what you can learn from it. If you’ve had similar experiences to mine, you may just find yourself having fun again, looking forward to playing with technology outside of your daily work life. All while still being able to achieve your goals and putting in your adulting time.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
    <item>
      <title>Dude, I'm slagging your code. Not you.</title>
      <dc:creator>Demian Brecht</dc:creator>
      <pubDate>Sat, 11 Apr 2020 19:31:04 +0000</pubDate>
      <link>https://dev.to/demianbrecht/dude-i-m-slagging-your-code-not-you-4nja</link>
      <guid>https://dev.to/demianbrecht/dude-i-m-slagging-your-code-not-you-4nja</guid>
      <description>&lt;p&gt;&lt;em&gt;I wrote this a few years ago on &lt;a href="https://engineering.salesforce.com/dude-im-slagging-your-code-not-you-f8b3f5769647" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;, but thought I'd re-share it with this community as I strongly believe that the experience and resulting realization were incredible for my career progression. Hope others can learn from or identify with it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I spent the first decade of my now roughly 16 years of writing code being overly attached to the code that I wrote. I took pride in what I designed and wrote. It was my baby. I’d spent hours painstakingly writing, refactoring, rinsing and repeating. I watched my code and libraries mature. I was generally happy with the output. Nobody could attack it without me immediately taking on the role of the parental defender. After all, that code was a part of me. It was the sum of all of my years of learning and practice. I was blinded by love. No child is cuter than your own.&lt;/p&gt;

&lt;p&gt;I’m sure many folks who have spent years writing code can identify with the above. Unfortunately, it’s really a horrible mindset to have in practice. Here’s a case study, based on an experience years ago from yours truly:&lt;/p&gt;

&lt;p&gt;I put an inordinate amount of time into writing an &lt;a href="https://github.com/demianbrecht/sanction" rel="noopener noreferrer"&gt;OAuth 2.0 client&lt;/a&gt; in Python (this is during spec draft days before many clients were available). There was another client available at the time, but it was a monster and really seemed like overkill to me. I spent a good deal of time digging through the RFCs, understanding the protocol to the lowest level. I spent just as much time researching deviations in provider implementations, which reminded me of early client side browser development days. I then spent a good amount of time designing and implementing a library that I was proud of. It beat the code footprint of the other library by over two orders of magnitude and as far as I could tell, offered just as much functionality.&lt;/p&gt;

&lt;p&gt;I understood that I’d written this library in a vacuum and wanted to get some feedback from someone more entrenched with the Python community. I’d watched a talk given by someone and enjoyed it, so decided to reach out to them and ask for a code review. Much to my horror, he ripped it apart. It felt like I was having my heart torn out and stomped on. How could he ever see anything wrong with this body of perfection that I’d created?! I found messages on his social media accounts poking at various areas in the code (not actually linking back to the code as to maintain anonymity). My jaw hit the floor. I couldn’t believe what had just happened.&lt;/p&gt;

&lt;p&gt;I fired back. I sent him an email directly with my feathers all ruffled. Something to the effect of “)&lt;em&gt;&amp;amp;#$ #$(&lt;/em&gt;)&amp;amp; #$)#(&lt;em&gt;&amp;amp;ASD&amp;amp;&lt;/em&gt;!!!”. His response was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Dude, I’m slagging your code. Not you.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I slept on it. While I still felt there’s a much better way to deliver feedback, it did force me to consider some bigger questions. How on earth could he have a clean separation between my person and my code? Weren’t they effectively one and the same as my code is a product of my efforts?&lt;/p&gt;

&lt;p&gt;Then the lightbulb went off. I was wrong. What I realized is that, while code that I’ve written is indeed mine, in no way, shape or form does it represent me as a person. Not only that, but no two software engineers will ever solve the same problem in the same way. The trick is to listen to what others are saying, to understand that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everyone solves problems differently,&lt;/li&gt;
&lt;li&gt;Everyone has a different delivery mechanism, with wildly varying degrees of tact, and&lt;/li&gt;
&lt;li&gt;They’re not talking about you, they’re talking about code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The person with the least amount of tact will say “this code is terrible”. They’re not calling you a jerk. They’re not saying you’re incompetent. They’re saying your code is suboptimal. Funny as it may sound, once you come to this realization, handling the most brutal code review or even having code that you’ve written entirely obliterated from source control becomes a cakewalk. No longer are you taking things folks are saying as personal attacks, but as sources of knowledge to enhance your own learnings and professional development. If someone says “this code sucks”, you tend to ask why they think that and how it can be improved rather than getting into arguments that are based on passion instead of sound technical reasoning. You’ll also likely be surprised by the number of times people think your code sucks because they don’t understand the problem domain well enough. You’ll be surprised at how often you can learn great stuff from those who are a little less savvy around conversational tact.&lt;/p&gt;

&lt;p&gt;Of course, I’m not advocating that ripping someone’s code apart verbally or over social media is a good thing by any stretch. It’s usually best to have one on one discussions with someone who’s reached out to you for a review. What I’m trying to get at here is the importance of being able to separate yourself from the code that you write. It’ll help you tons throughout your career.&lt;/p&gt;

</description>
      <category>career</category>
      <category>reviews</category>
      <category>productivity</category>
      <category>mentorship</category>
    </item>
    <item>
      <title>Entry points in Python</title>
      <dc:creator>Demian Brecht</dc:creator>
      <pubDate>Thu, 09 Apr 2020 19:56:27 +0000</pubDate>
      <link>https://dev.to/demianbrecht/entry-points-in-python-34i3</link>
      <guid>https://dev.to/demianbrecht/entry-points-in-python-34i3</guid>
      <description>&lt;h3&gt;
  
  
  What's an entry point?
&lt;/h3&gt;

&lt;p&gt;The most relevant bit of the description in &lt;a href="https://en.wikipedia.org/wiki/Entry_point" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt; is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In computer programming, an entry point is where the first instructions of a program are executed, and where the program has access to command line arguments. To start a program's execution, the loader or operating system passes control to its entry point.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What we're going to cover in this blog post is the various methods that you can define entry points in your Python programs. Some with obvious downsides as we'll discover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it simple: Python scripts
&lt;/h2&gt;

&lt;p&gt;The simplest thing that you can do is treat your Python file as a self-contained script. Here's the old "Hello world!" example:&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;#!/usr/bin/env python
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hi&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello world!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;say_hi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this file directly gives us an implicit entry point: Just by running the script we get right into execution:&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;./hellow_world.py
Hello world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy, right? So what's the problem here?&lt;/p&gt;

&lt;h3&gt;
  
  
  The downside
&lt;/h3&gt;

&lt;p&gt;What if your project became complex enough to warrant multiple modules? What if you want to import and say hello from another module? With the above, this is what would happen:&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="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hello_world&lt;/span&gt;                                                                                                                                                          
&lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the above, the problem with the Python script approach is that it's overly simple and functionality is executed at &lt;strong&gt;import time&lt;/strong&gt;, which is typically not what you're after. Not only is it functionally not what you're going to be after, but writing unit test for it will be difficult if not impossible, especially if there is any state change through side effects.&lt;/p&gt;

&lt;p&gt;So how can we solve this? Read on!&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveling up: What's my name?
&lt;/h2&gt;

&lt;p&gt;So now we know that sticking all of our functionality in a Python script isn't the best approach if we want to reuse or write unit tests. So then, what's the next best thing? Using the if &lt;strong&gt;name&lt;/strong&gt; == '&lt;strong&gt;main&lt;/strong&gt;' pattern.&lt;/p&gt;

&lt;p&gt;From the &lt;a href="https://docs.python.org/3/library/__main__.html" rel="noopener noreferrer"&gt;Python docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;'__main__'&lt;/code&gt; is the name of the scope in which top-level code executes. A module’s name is set equal to &lt;code&gt;'__main__'&lt;/code&gt; when read from standard input, a script, or from an interactive prompt. A module can discover whether or not it is running in the main scope by checking its own &lt;strong&gt;name&lt;/strong&gt;, which allows a common idiom for conditionally executing code in a module when it is run as a script or with &lt;code&gt;python -m&lt;/code&gt; but not when it is imported.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we adopt this new learning to &lt;code&gt;hello_world.py&lt;/code&gt;, it would then look like this:&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;#!/usr/bin/env python
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hi&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello world!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;say_hi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, now running it from the command line will still yield the same result:&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;./hellow_world.py
Hello world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The major difference though, is that now our import time problem has been solved. Rather than seeing "Hello world!" at module import, we have to explicitly call &lt;code&gt;say_hi()&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="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hello_world&lt;/span&gt;                                                                                                                                                          
&lt;span class="c1"&gt;# No output here anymore!
&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                                                                                                                                                        
&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will now also allow us to write and run unit tests against the &lt;code&gt;hello_world&lt;/code&gt; module without having to worry about import time side effects.&lt;/p&gt;

&lt;p&gt;The observant reader will notice that the Python docs also refers to running Python modules directly with &lt;code&gt;python -m&lt;/code&gt;. This would allow you to run &lt;code&gt;hello_world&lt;/code&gt; without making it executable and adding the shebang at the beginning of the file:&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;python &lt;span class="nt"&gt;-m&lt;/span&gt; hello_world
Hello world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example of this is the standard module's &lt;code&gt;json.tool&lt;/code&gt;:&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;python &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'{"hello": "world"}'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"hello"&lt;/span&gt;: &lt;span class="s2"&gt;"world"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"This is awesome! All my problems are solved!" says you. "Nay" says I. "Not quite yet".&lt;/p&gt;

&lt;h3&gt;
  
  
  The downside
&lt;/h3&gt;

&lt;p&gt;Now we have entry points in Python files that we can unit test against. Great. But what are we to do if our project &lt;strong&gt;really&lt;/strong&gt; increases in size? How do we find which modules have entry points defined and which ones don't? There &lt;strong&gt;must&lt;/strong&gt; be a better option than:&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;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"if __name__&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*==&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*['&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]__main__['&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indeed there is, dear reader. Indeed there is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Come together: Script organization
&lt;/h2&gt;

&lt;p&gt;Now that we know about the &lt;code&gt;if __name__ == '__main__'&lt;/code&gt; pattern, how do we avoid the confusing aspect of figuring out which files are actually executable? Well, this one has two facets, both of which are fine on their own but shine when used together:&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a &lt;code&gt;scripts&lt;/code&gt; directory to your project
&lt;/h3&gt;

&lt;p&gt;By adding a &lt;code&gt;scripts&lt;/code&gt; directory, you can lump all of your entry points into a single directory, making it ridiculously simple for anyone looking at your project to find all of the various entry points for your project. After doing this with &lt;code&gt;hello_world.py&lt;/code&gt;, this is what our directory structure looks like:&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;tree &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── scripts
│   └── hello_world.py
└── setup.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a new user or developer just coming into the project, this makes it tremendously simple to figure out what entry points are available in your project.&lt;/p&gt;

&lt;p&gt;How can we enhance this experience you ask? That part comes with the introduction of &lt;code&gt;setuptools&lt;/code&gt;. In the above, you'll notice a &lt;code&gt;setup.py&lt;/code&gt; file. It's within there that we want to define &lt;code&gt;scripts&lt;/code&gt; to be installed when the user installs your package:&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="n"&gt;setuptools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello_world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.1.0,
    scripts=[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hello_world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]
)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For ease of use, I've also dropped the extension in &lt;code&gt;hello_world&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now when a user installs the package, here's what they'll see:&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;python setup.py develop
running develop
running egg_info
creating hello_world.egg-info
writing hello_world.egg-info/PKG-INFO
writing dependency_links to hello_world.egg-info/dependency_links.txt
writing top-level names to hello_world.egg-info/top_level.txt
writing manifest file &lt;span class="s1"&gt;'hello_world.egg-info/SOURCES.txt'&lt;/span&gt;
reading manifest file &lt;span class="s1"&gt;'hello_world.egg-info/SOURCES.txt'&lt;/span&gt;
writing manifest file &lt;span class="s1"&gt;'hello_world.egg-info/SOURCES.txt'&lt;/span&gt;
running build_ext
Creating /path/to/.venv/lib/python3.7/site-packages/hello_world.egg-link &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;link &lt;/span&gt;to .&lt;span class="o"&gt;)&lt;/span&gt;
Adding hello 0.1.0 to easy-install.pth file
Installing hello_world script to /path/to/.venv/bin

Installed /path/to/hello_world
Processing dependencies &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nv"&gt;hello_world&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.1.0
Finished processing dependencies &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nv"&gt;hello_world&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing to note in the above is &lt;code&gt;Installing hello_world script to /path/to/.venv/bin&lt;/code&gt;. This means that users now don't have to fiddle with running &lt;code&gt;./scripts/hello_world&lt;/code&gt; or &lt;code&gt;python -m hello_world&lt;/code&gt;, they can simply run the script directly:&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;hello_world 
Hello world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"What is this magic?!" you ask?&lt;/p&gt;

&lt;p&gt;For each entry in the &lt;code&gt;scripts&lt;/code&gt; entry, a file will be created in your environment's Python &lt;code&gt;bin&lt;/code&gt; dir. The resulting file looks like this:&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;#!/path/to/.venv/bin/python3.7
# EASY-INSTALL-DEV-SCRIPT: 'hello_world==0.1.0','hello_world'
&lt;/span&gt;&lt;span class="n"&gt;__requires__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello==0.1.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="nf"&gt;__import__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pkg_resources&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello==0.1.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;__file__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/path/to/hello/scripts/hello_world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the executable script written to the bin directory effectively acts as a redirect to your source file, executing it.&lt;/p&gt;

&lt;p&gt;The combination of the approaches to this point should be enough to satisfy even complex projects. However, if you want to know a completely different option (and my personal favorite), read on!&lt;/p&gt;

&lt;h2&gt;
  
  
  And now for something completely different: &lt;code&gt;entry_points&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The combination of &lt;code&gt;console_scripts&lt;/code&gt; and &lt;code&gt;entry_points&lt;/code&gt; in a project's &lt;code&gt;setup.py&lt;/code&gt; can help us achieve the same results as the previous sections, but without the need for the &lt;code&gt;if __name__ == '__main__'&lt;/code&gt; pattern. This is achieved by specifying a callable entry points in the &lt;code&gt;setuptools&lt;/code&gt; config:&lt;/p&gt;

&lt;p&gt;Noting that we're back to the original directory structure:&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;tree &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── hello_world.py
└── setup.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;setup.py&lt;/code&gt; file might now look something like this:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setuptools&lt;/span&gt;

&lt;span class="n"&gt;setuptools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello_world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.1.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;entry_points&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;console_scripts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello_world=hello_world:say_hi&lt;/span&gt;&lt;span class="sh"&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The observant reader will notice that as we're configuring the entry point to be a callable, this is now something that can easily be unit tested directly.&lt;/p&gt;

&lt;p&gt;The magic sauce that allows us to run the script directly looks a little different than the treatment it gets from &lt;code&gt;scripts&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="c1"&gt;# EASY-INSTALL-ENTRY-SCRIPT: 'hello-world','console_scripts','hello_world'
&lt;/span&gt;&lt;span class="n"&gt;__requires__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello-world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pkg_resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_entry_point&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(-script\.pyw?|\.exe)?$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;load_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello-world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;console_scripts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello_world&lt;/span&gt;&lt;span class="sh"&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;Definitely has some functional differences, but at a high level the results are effectively the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ hello_world 
Hello world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gettin' jiggy with it: Single entry point
&lt;/h2&gt;

&lt;p&gt;"So I have a couple ways to define entry points" you say. "But what if I want to have multiple entry points? Does that mean that I need to have multiple executables for my single application?"&lt;/p&gt;

&lt;p&gt;Great question! And of course, the answer is "no", but how you solve that problem is entirely up to you.&lt;/p&gt;

&lt;p&gt;An approach that I've used in the past is to define a single entry point and to create a &lt;code&gt;commands&lt;/code&gt; directory and a module for each sub-command I want available.&lt;/p&gt;

&lt;p&gt;The project structure would now look like this:&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;tree &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── hello_world
│   ├── app.py
│   ├── commands
│   │   ├── __init__.py
│   │   └── say_hi.py
│   └── __init__.py
└── setup.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;setup.py&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setuptools&lt;/span&gt;

&lt;span class="n"&gt;setuptools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello_world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.1.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;entry_points&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;console_scripts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello_world=hello_world.app:main&lt;/span&gt;&lt;span class="sh"&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;app.py&lt;/code&gt; module is simplistic, but captures one potential approach (how it does what it does is an exercise left to the reader):&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;importlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Say hello to the world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Which command to run&lt;/span&gt;&lt;span class="sh"&gt;'&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="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;importlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;import_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello_world.commands.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ModuleNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Invalid command&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Command&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)().&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the actual command (&lt;code&gt;say_hi.py&lt;/code&gt;) looks like this:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello world!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have multiple commands, which I would hope you are if you're using this approach, then you could either rely on duck typing as I am above, or define an abstract base class and ensure the command being imported in &lt;code&gt;app.py&lt;/code&gt; is an instance of that interface. The latter is likely the better of the approaches if multiple people are contributing to the code base.&lt;/p&gt;

&lt;p&gt;Then running it after installing becomes:&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;hello_world say_hi
Hello world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or... Y'know... You could use a sane library like &lt;a href="https://pypi.org/project/click/" rel="noopener noreferrer"&gt;click&lt;/a&gt; to do this instead of rebuilding the wheel.&lt;/p&gt;

&lt;h2&gt;
  
  
  You're just a wee bit more dangerous now!
&lt;/h2&gt;

&lt;p&gt;Hopefully you've learned some new tips and tricks here and your next project won't make your new hire want to pull their hair out trying to figure out how the entry points in your project work.&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why the hell is my code so slow?! (aka performance analysis in Python)</title>
      <dc:creator>Demian Brecht</dc:creator>
      <pubDate>Tue, 10 Mar 2020 05:56:15 +0000</pubDate>
      <link>https://dev.to/demianbrecht/why-the-hell-is-my-code-so-slow-aka-performance-analysis-in-python-1c7d</link>
      <guid>https://dev.to/demianbrecht/why-the-hell-is-my-code-so-slow-aka-performance-analysis-in-python-1c7d</guid>
      <description>&lt;h2&gt;
  
  
  Setting the stage
&lt;/h2&gt;

&lt;p&gt;So you've finally finished that feature that you've been grinding on for a while. It's polished up. It's passed the unit tests you've written for it locally. It's passed your local integration tests. The pull request has been reviewed and iterated on. You're happy you still have hair left after a few back-and-forths with the team pedant who kept hitting "request changes" in their review even though everyone else on the list approved it already. Your pull request has been finally been accepted by all. It's time to merge it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You hit the button.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It's in master.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You're proud of the work you've done as well you should be. You've put in the time and the effort into understanding what your producer, product owner or customer was looking for and bridging the technical requirements to pull it off successfully.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nothing broke the master build.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You go have a celebratory scotch that night. Or a glass of wine. Or a coffee. Or a water. Whatever. Your feature has been deployed and is being used by the general public. This is awesome. Another feather in your cap.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And then you get the email.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks for all your hard work on this great new feature! We haven't seen any new bugs come out of it as a result and no complaints from our users. Awesome work! Buuuut, we were noticing some slowness when using the feature and dug into it a bit. Seems like the main endpoint you wrote is causing our average response time down by 500ms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your world crumbles around you. You can feel the heat as your face reddens. This feels worse than breaking master. How did this happen?! How on Earth are you going to solve this problem?&lt;/p&gt;

&lt;p&gt;Sound vaguely familiar? Not experienced it and are hoping to avoid as much as possible in the future? Hoping to get past this completely unnecessary possibly fictional story in order to find some nuggets that may help in the future? Never fear, I'm now finished attempting to needlessly blow up the reading time of this post!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The astute reader may notice the distinct lack of load tests in the above story. Of course, this &lt;strong&gt;never&lt;/strong&gt; happens in real life... &lt;strong&gt;RIGHT?!&lt;/strong&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The feature
&lt;/h2&gt;

&lt;p&gt;Let's say the amalgamation of my blood, sweat and possibly a few tears is incredibly complex. The feature that was merged into master and brought my team's servers near to a state of &lt;a href="https://en.wikipedia.org/wiki/Denial-of-service_attack" rel="noopener noreferrer"&gt;DoS&lt;/a&gt; looked like this:&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;print_this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value_b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{} did this thing to {}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value_b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The observant reader will notice that this is in no, way, shape or form complex code. However, it should be enough to demonstrate my typical approach to investigating and solving performance issues)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's slow?
&lt;/h2&gt;

&lt;p&gt;If what's causing the bottleneck is unknown to me, my first stop will typically be &lt;a href="https://docs.python.org/3/library/profile.html" rel="noopener noreferrer"&gt;&lt;code&gt;cProfile&lt;/code&gt;&lt;/a&gt;. There are a few different methods of running &lt;code&gt;cProfile&lt;/code&gt; and printing statistics, but I'm only going to concentrate on the process that I typically use&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;First, I'll instrument the entry and exit points of the code. In this incredibly complex case, it'll be where &lt;code&gt;print_this&lt;/code&gt; is called:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cProfile&lt;/span&gt;

&lt;span class="n"&gt;profiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;profiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print_this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bar&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;profiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;profiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;print_this.profile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;It's worthwhile to note that Python 3.8 introduced a &lt;a href="https://docs.python.org/3/reference/datamodel.html#context-managers" rel="noopener noreferrer"&gt;context manager&lt;/a&gt; implementation which is pretty nifty, but I wrote this using 3.7 so we're just going to have to deal with the archaic method.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The results of the profiling will be dumped to the file &lt;code&gt;print_this.profile&lt;/code&gt; for analysis by other tools, namely &lt;a href="https://docs.python.org/3/library/profile.html#module-pstats" rel="noopener noreferrer"&gt;&lt;code&gt;pstats&lt;/code&gt;&lt;/a&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;. Alternatively, you can print the results directly using &lt;code&gt;profiler.print_stats()&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="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pstats&lt;/span&gt;                                                                                                                                                                       

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pstats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;print_this.profile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                                                                                                                          
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cumulative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;print_stats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                                                                                                                                        
&lt;span class="n"&gt;Mon&lt;/span&gt; &lt;span class="n"&gt;Mar&lt;/span&gt;  &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt;    &lt;span class="n"&gt;print_this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;

         &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;

   &lt;span class="n"&gt;Ordered&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cumulative&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

   &lt;span class="n"&gt;ncalls&lt;/span&gt;  &lt;span class="n"&gt;tottime&lt;/span&gt;  &lt;span class="n"&gt;percall&lt;/span&gt;  &lt;span class="n"&gt;cumtime&lt;/span&gt;  &lt;span class="n"&gt;percall&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.005&lt;/span&gt;    &lt;span class="mf"&gt;0.005&lt;/span&gt; &lt;span class="n"&gt;print_this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;print_this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;    &lt;span class="mf"&gt;0.005&lt;/span&gt;    &lt;span class="mf"&gt;0.005&lt;/span&gt;    &lt;span class="mf"&gt;0.005&lt;/span&gt;    &lt;span class="mf"&gt;0.005&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;str&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;built&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;builtins&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="mi"&gt;1&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;disable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_lsprof.Profiler&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I usually sort by cumulative time to get an idea of what the problematic call stack might look like (you can find the other available values and descriptions of each &lt;a href="https://docs.python.org/3/library/profile.html#pstats.Stats.sort_stats" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A worthy mention here is that you can also limit the number of lines output. Integers will limit to line count while a fraction between 0.0 and 1.0 will output a percentage. See &lt;a href="https://docs.python.org/3/library/profile.html#pstats.Stats.print_stats" rel="noopener noreferrer"&gt;here&lt;/a&gt; for other possible filters.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The astute reader will shame me for the numbers in the above table. To be clear, the numbers are fudged for the sake of keeping the code simple.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Looking at the output from &lt;code&gt;pstats&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AHA!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I can easily tell what's chewing up the time. There are two functions at the top of the pile, one of which has a &lt;code&gt;percall&lt;/code&gt; and &lt;code&gt;tottime&lt;/code&gt; of &lt;code&gt;0.000&lt;/code&gt;, but cumulative time of &lt;code&gt;0.005&lt;/code&gt;. This tells me that this is isn't the target but is part of the call stack. On the other hand, &lt;code&gt;{method 'format' of 'str' objects}&lt;/code&gt; is taking a ridiculous &lt;code&gt;0.005&lt;/code&gt;s per call! Problem found! Call in the cavalry!&lt;/p&gt;

&lt;p&gt;Oh wait... I'm the cavalry. Crap.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what to do?
&lt;/h2&gt;

&lt;p&gt;Thankfully (and completely intentionally), the scope of this particular performance issue is small. It's limited to a single line. In real life, it will almost certainly be more complex, so brace yourself for iterative code profile instrumentation and experiments. It's not always a simple task to nail down performance issues, but it's oh so satisfying when you've identified and squashed a bottleneck.&lt;/p&gt;

&lt;p&gt;In this case, I know that the problem with the performance is the call to &lt;code&gt;&amp;lt;str&amp;gt;.format()&lt;/code&gt;, so I have to figure out a replacement for it. Again, thankfully (and again, entirely intentionally) I'm aware of a few different methods that it can be replaced with. But which one should I use? The choices I have available to me are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concatenation (&lt;code&gt;value_a + ' did this thing to ' + value_b&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;interpolation (&lt;code&gt;'%s did this thing to %s' % (value_a, value_b)&lt;/code&gt;), and&lt;/li&gt;
&lt;li&gt;f-strings (&lt;code&gt;f'{value_a} did this thing to {value_b}'&lt;/code&gt;) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than having to iteratively refactor, instrument, output and analyze performance metrics, I can use a simple tool I have at my disposal: &lt;a href="https://docs.python.org/3/library/timeit.html" rel="noopener noreferrer"&gt;&lt;code&gt;timeit&lt;/code&gt;&lt;/a&gt;:&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="c"&gt;# Concatenation&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-n&lt;/span&gt; 500000 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"value_a, value_b = ('foo', 'bar')"&lt;/span&gt; &lt;span class="s2"&gt;"value_a + ' did this thing to ' + value_b"&lt;/span&gt;
500000 loops, best of 5: 65.2 nsec per loop

&lt;span class="c"&gt;# Interpolation&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-n&lt;/span&gt; 500000 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"value_a, value_b = ('foo', 'bar')"&lt;/span&gt; &lt;span class="s2"&gt;"'%s did this thing to %s' % (value_a, value_b)"&lt;/span&gt;
500000 loops, best of 5: 134 nsec per loop

&lt;span class="c"&gt;# F-Strings&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; timeit &lt;span class="nt"&gt;-n&lt;/span&gt; 500000 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"value_a, value_b = ('foo', 'bar')"&lt;/span&gt; &lt;span class="s2"&gt;"f'{value_a} did this thing to {value_b}'"&lt;/span&gt;
500000 loops, best of 5: 56.7 nsec per loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;It's also possible to import and use &lt;code&gt;timeit&lt;/code&gt; from within a Python shell. Check out the interface &lt;a href="https://docs.python.org/3/library/timeit.html#python-interface" rel="noopener noreferrer"&gt;here&lt;/a&gt; if interested.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two notables in the above example are:&lt;/p&gt;

&lt;p&gt;a) I'm explicit about the number of loops to run (&lt;code&gt;-n&lt;/code&gt;)&lt;br&gt;
b) I don't want to measure the cost of variable assignment (i.e. &lt;code&gt;value_a = 'foo'&lt;/code&gt;), so I pull that part out into the setup argument (&lt;code&gt;-s&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Based on this, I can easily tell which method I should use in my code: f-strings!&lt;/p&gt;
&lt;h2&gt;
  
  
  Righting the wrongs
&lt;/h2&gt;

&lt;p&gt;So now that I know what needs to change, those changes are made:&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;print_this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value_b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&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="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value_a&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; did this thing to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value_b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just for sanity sake, I re-run the instrumented code and load and dump out the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;In &lt;span class="o"&gt;[&lt;/span&gt;1]: import pstats                                                                                                                                                                    
In &lt;span class="o"&gt;[&lt;/span&gt;2]: stats &lt;span class="o"&gt;=&lt;/span&gt; pstats.Stats&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'print_this.profile'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                                                                                                                               
In &lt;span class="o"&gt;[&lt;/span&gt;3]: stats.sort_stats&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cumulative'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.print_stats&lt;span class="o"&gt;()&lt;/span&gt;                                                                                                                                     
Mon Mar  9 22:02:13 2020    print_this_fstring.profile

         3 &lt;span class="k"&gt;function &lt;/span&gt;calls &lt;span class="k"&gt;in &lt;/span&gt;0.000 seconds

   Ordered by: cumulative &lt;span class="nb"&gt;time

   &lt;/span&gt;ncalls  tottime  percall  cumtime  percall filename:lineno&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        1    0.000    0.000    0.000    0.000 print_this.py:1&lt;span class="o"&gt;(&lt;/span&gt;print_this&lt;span class="o"&gt;)&lt;/span&gt;
        1    0.000    0.000    0.000    0.000 &lt;span class="o"&gt;{&lt;/span&gt;built-in method builtins.print&lt;span class="o"&gt;}&lt;/span&gt;
        1    0.000    0.000    0.000    0.000 &lt;span class="o"&gt;{&lt;/span&gt;method &lt;span class="s1"&gt;'disable'&lt;/span&gt; of &lt;span class="s1"&gt;'_lsprof.Profiler'&lt;/span&gt; objects&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And indeed, the performance problem has been resolved. Huzzah! Not only has my feature been shipped, but I've successfully been able to resolve a nasty performance issue! Pull request has been created along with details of the performance analysis, accepted and merged. &lt;em&gt;Now&lt;/em&gt; I can go go home and partake in that celebratory scotch. Or glass of wine. Or coffee. Or water. Whatever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hold on, WHY is &lt;code&gt;&amp;lt;str&amp;gt;&lt;/code&gt;.format() slow?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;If you're not so interested, feel free to stop reading here as you likely won't need to use the following tips 'n tricks in your daily life of performance analysis.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For those of you who are interested, oh I'm so glad that you asked! When trying to figure out why solution A is better than solution B, there's no better method than using the &lt;a href="https://docs.python.org/3/library/dis.html" rel="noopener noreferrer"&gt;&lt;code&gt;dis&lt;/code&gt;&lt;/a&gt; module. For our purposes, here's what string formatting and f-strings look like in Python 3.7:&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="c"&gt;# String formatting&lt;/span&gt;
In &lt;span class="o"&gt;[&lt;/span&gt;1]: from print_this import print_this                                                                                                                                               
In &lt;span class="o"&gt;[&lt;/span&gt;2]: from dis import dis                                                                                                                                                             
In &lt;span class="o"&gt;[&lt;/span&gt;3]: dis&lt;span class="o"&gt;(&lt;/span&gt;print_this&lt;span class="o"&gt;)&lt;/span&gt;         
  2           0 LOAD_GLOBAL              0 &lt;span class="o"&gt;(&lt;/span&gt;print&lt;span class="o"&gt;)&lt;/span&gt;
              2 LOAD_CONST               1 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'{} did this thing to {}'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              4 LOAD_METHOD              1 &lt;span class="o"&gt;(&lt;/span&gt;format&lt;span class="o"&gt;)&lt;/span&gt;
              6 LOAD_FAST                0 &lt;span class="o"&gt;(&lt;/span&gt;value_a&lt;span class="o"&gt;)&lt;/span&gt;
              8 LOAD_FAST                1 &lt;span class="o"&gt;(&lt;/span&gt;value_b&lt;span class="o"&gt;)&lt;/span&gt;
             10 CALL_METHOD              2
             12 CALL_FUNCTION            1
             14 POP_TOP
             16 LOAD_CONST               0 &lt;span class="o"&gt;(&lt;/span&gt;None&lt;span class="o"&gt;)&lt;/span&gt;
             18 RETURN_VALUE


&lt;span class="c"&gt;# F-Strings&lt;/span&gt;
In &lt;span class="o"&gt;[&lt;/span&gt;1]: from print_this import print_this                                                                                                                                                
In &lt;span class="o"&gt;[&lt;/span&gt;2]: from dis import dis                                                                                                                                                              
In &lt;span class="o"&gt;[&lt;/span&gt;3]: dis&lt;span class="o"&gt;(&lt;/span&gt;print_this&lt;span class="o"&gt;)&lt;/span&gt;                                                                                                                                                                  
  2           0 LOAD_GLOBAL              0 &lt;span class="o"&gt;(&lt;/span&gt;print&lt;span class="o"&gt;)&lt;/span&gt;
              2 LOAD_FAST                0 &lt;span class="o"&gt;(&lt;/span&gt;value_a&lt;span class="o"&gt;)&lt;/span&gt;
              4 FORMAT_VALUE             0
              6 LOAD_CONST               1 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' did this thing to '&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              8 LOAD_FAST                1 &lt;span class="o"&gt;(&lt;/span&gt;value_b&lt;span class="o"&gt;)&lt;/span&gt;
             10 FORMAT_VALUE             0
             12 BUILD_STRING             3
             14 CALL_FUNCTION            1
             16 POP_TOP
             18 LOAD_CONST               0 &lt;span class="o"&gt;(&lt;/span&gt;None&lt;span class="o"&gt;)&lt;/span&gt;
             20 RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now unfortunately for our purposes, it isn't immediately obvious what the difference is between the two. However, this also presents us the opportunity to dig into the interpreter implementation and at least derive a theory&lt;sup id="fnref3"&gt;3&lt;/sup&gt;:&lt;/p&gt;

&lt;p&gt;The notable differences between the two are that string formatting calls &lt;code&gt;LOAD_METHOD&lt;/code&gt; and &lt;code&gt;CALL_METHOD&lt;/code&gt;, while the f-string implementation simply calls &lt;code&gt;FORMAT_VALUE&lt;/code&gt;. Looking at those opcodes (defined in &lt;a href="https://github.com/python/cpython/blob/master/Include/opcode.h" rel="noopener noreferrer"&gt;opcode.h&lt;/a&gt; and their respective implementations in &lt;a href="https://github.com/python/cpython/blob/master/Python/ceval.c" rel="noopener noreferrer"&gt;ceval.c&lt;/a&gt;, we can relatively safely come to the conclusion that &lt;a href="https://github.com/python/cpython/blob/3.7/Python/ceval.c#L3034" rel="noopener noreferrer"&gt;&lt;code&gt;LOAD_METHOD&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/python/cpython/blob/3.7/Python/ceval.c#L3071" rel="noopener noreferrer"&gt;&lt;code&gt;CALL_METHOD&lt;/code&gt;&lt;/a&gt; are far more costly than &lt;a href="https://github.com/python/cpython/blob/3.7/Python/ceval.c#L3254" rel="noopener noreferrer"&gt;&lt;code&gt;FORMAT_VALUE&lt;/code&gt;&lt;/a&gt;, which actually formats the entire string directly in the opcode handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping things up
&lt;/h2&gt;

&lt;p&gt;Going into this depth of performance analysis isn't always required. Most of the times things will work to an acceptable level of performance. Having these tools in your back pocket can sometimes promote the want to analyze everything you write. However, one should always remember that "premature optimization is the root of all evil"!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Feel free to heckle in the comments ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;I prefer using &lt;code&gt;pstats&lt;/code&gt; to dumping stats directly because of the additional niceties it offers. Also because I frequently screw up and need to have a stable copy of the profile output. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Interpreter execution is not an area of expertise for me, so I very much welcome feedback here. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>python</category>
      <category>refactorit</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The lesser known (but incredibly useful) way of settings breakpoints in Python</title>
      <dc:creator>Demian Brecht</dc:creator>
      <pubDate>Tue, 03 Mar 2020 21:15:35 +0000</pubDate>
      <link>https://dev.to/demianbrecht/the-lesser-known-way-of-settings-breakpoints-in-python-2lll</link>
      <guid>https://dev.to/demianbrecht/the-lesser-known-way-of-settings-breakpoints-in-python-2lll</guid>
      <description>&lt;p&gt;Everyone knows that in order to set a breakpoint in Python you need to modify your source code with the somewhat unintuitive&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# or if &amp;gt;= 3.7
&lt;/span&gt;&lt;span class="nf"&gt;breakpoint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Did you know that there's a way to set breakpoints that &lt;em&gt;doesn't&lt;/em&gt; involve modifying your code?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"What's this silliness you speak of?"&lt;/em&gt; you say?&lt;/p&gt;

&lt;p&gt;There may be times when you have to jump through ridiculous hoops in order to modify code&lt;sup id="fnref1"&gt;1&lt;/sup&gt; and the more common method of setting breakpoints in Python isn't worthwhile. For those cases, you have the ability to run your code interactively through a &lt;code&gt;pdb&lt;/code&gt; shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m pdb &amp;lt;script&amp;gt;.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's right, &lt;code&gt;pdb&lt;/code&gt; can be run as an executable module! Neat-o, right? Now you'll be dropped into the &lt;code&gt;pdb&lt;/code&gt; shell, from which you can run your usual &lt;code&gt;pdb&lt;/code&gt; commands (&lt;code&gt;n&lt;/code&gt;ext, &lt;code&gt;l&lt;/code&gt;ist, &lt;code&gt;c&lt;/code&gt;ontinue, etc). For our particular use case however, the most interesting and useful command is &lt;code&gt;b&lt;/code&gt;reak:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    b(reak) [ ([filename:]lineno | function) [, condition] ]
            Without argument, list all breaks.

            With a line number argument, set a break at this line in the
            current file.  With a function name, set a break at the first
            executable line of that function.  If a second argument is
            present, it is a string specifying an expression which must
            evaluate to true before the breakpoint is honored.

            The line number may be prefixed with a filename and a colon,
            to specify a breakpoint in another file (probably one that
            hasn't been loaded yet).  The file is searched for on
            sys.path; the .py suffix may be omitted.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;b&lt;/code&gt;reak command is incredibly flexible. You can specify breakpoint locations by line number (prefixing it with a filename is optional), by function and even slap on a condition.&lt;/p&gt;

&lt;p&gt;As an example, consider the following &lt;code&gt;example.py&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bar&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://example.com/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting a breakpoint with (filename):lineno
&lt;/h3&gt;

&lt;p&gt;This can be useful when we know the exact file and line we want to break execution at. This can be really useful if you want to break into the debugger shell at import time rather than execution (debugging circular references, etc).&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;Breakpoint&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&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;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting a breakpoint by function
&lt;/h3&gt;

&lt;p&gt;This is generally the easiest way to set breakpoints, especially when dealing with dependencies. For this example, let's say we already know that &lt;code&gt;requests.get&lt;/code&gt; eventually calls into &lt;code&gt;requests.request&lt;/code&gt; and we want to stop at that point rather than having to step through preamble code in &lt;code&gt;requests.get&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="n"&gt;Breakpoint&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;
  &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;locals&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://example.com/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;It's worthwhile to note that it's possible to import and set breakpoints in dependencies even before your code has done any importing itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting a breakpoint with a condition
&lt;/h3&gt;

&lt;p&gt;Let's say we want to break in the example file's &lt;code&gt;bar&lt;/code&gt; method, but &lt;em&gt;only&lt;/em&gt; when the parameter value is equal to &lt;code&gt;"foo"&lt;/code&gt;. This can be incredibly useful when we don't want to break into program execution on every iteration of a function or method:&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;Breakpoint&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting a temporary breakpoint
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tbreak&lt;/code&gt; is another useful way to set breakpoints if we only want to break into execution once. The breakpoint is then automatically discarded. In this example, we only want to cause a break at the first call into &lt;code&gt;bar&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;tbreak&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;
&lt;span class="n"&gt;Breakpoint&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="n"&gt;Deleted&lt;/span&gt; &lt;span class="n"&gt;breakpoint&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbrecht&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playground&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bar&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;These methods have definitely been useful to me in the past. If you didn't already know about them, hopefully they will be a new tool to add to your debugging arsenal!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;For instance, when running on an intentionally immutable container ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>You will pry vim from my cold, dead hands</title>
      <dc:creator>Demian Brecht</dc:creator>
      <pubDate>Tue, 03 Mar 2020 20:13:31 +0000</pubDate>
      <link>https://dev.to/demianbrecht/you-will-pry-vim-from-my-cold-dead-hands-29d6</link>
      <guid>https://dev.to/demianbrecht/you-will-pry-vim-from-my-cold-dead-hands-29d6</guid>
      <description>&lt;p&gt;I get funny looks from people quite often. &lt;/p&gt;

&lt;p&gt;Random person: &lt;em&gt;"What IDE do you use?"&lt;/em&gt;&lt;br&gt;
Me: &lt;em&gt;"I don't use an IDE unless I absolutely have to. I use vim."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's that funny look again.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Why on Earth would you use something so archaic?!"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"How can you live without a GUI-driven debugger?!"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Why would you want to spend half your life configuring your editor?!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Those are just a few of the typical questions that accompany those funny looks. Here are my typical answers for posterity's sake:&lt;/p&gt;
&lt;h3&gt;
  
  
  On using an archaic editor
&lt;/h3&gt;

&lt;p&gt;Yes, it's archaic. But hey, there are advantages to being archaic other than being able to pull the old "I've been doing this longer than you've been alive" line. &lt;em&gt;Because&lt;/em&gt; it's archaic, it's literally the single most commonly available editor on *nix-based systems. If &lt;code&gt;vim&lt;/code&gt; isn't available, you can usually rest assured that &lt;code&gt;vi&lt;/code&gt; is. If you're accustomed to most of &lt;code&gt;vim&lt;/code&gt;'s basic features, you won't feel entirely out of place in &lt;code&gt;vi&lt;/code&gt;. In today's world of distributed systems, having the ability to speak nearly each and every distro's editor language is an incredible useful skill. I feel as much at home editing on a RHEL system as I do on Debian as I do on OSX.&lt;/p&gt;
&lt;h3&gt;
  
  
  On living life without a GUI-driven debugger
&lt;/h3&gt;

&lt;p&gt;I've actually found that there's elegance and freedom in using command-line debuggers directly. Yes, there is a relatively steep learning curve (I'm looking squarely at &lt;em&gt;you&lt;/em&gt;, &lt;code&gt;gdb&lt;/code&gt;). Yes, setting a breakpoint will likely take longer when on the command line or may be a little unintuitive (I'm looking squarely at &lt;em&gt;you&lt;/em&gt; &lt;code&gt;import pdb; pdb.set_trace()&lt;/code&gt;). And yes, once you get into bed and begin to love using command line debuggers, you may catch yourself spending hours trying to continue living in your beautiful, magical world with languages that don't support it or support it very well (I'm looking squarely at &lt;em&gt;you&lt;/em&gt; Java).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But&lt;/em&gt;, once you get past those hurdles, the flexibility and power that you have at your fingertips is really unparalleled by anything else. Oh, and did I mention that debugging on a remote machine is as easy as SSH'ing into it and running the debugger in the shell&lt;sup id="fnref1"&gt;1&lt;/sup&gt;? Good luck getting your &amp;lt;insert favorite IDE here&amp;gt; to do that across bastions or docker containers&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  On spending half your life in config
&lt;/h3&gt;

&lt;p&gt;Well, here's the beautiful thing: I don't. To be fair, there was a day that I spent a ridiculous amount of time configuring a &lt;code&gt;zsh&lt;/code&gt; shell and ran all ridiculous number of &lt;code&gt;vim&lt;/code&gt; plugins to be all 1337. Problem is, they're not easily portable, especially when you start throwing ephemeral containers and VMs into the mix. I learn to rely on the near bare bones offerings that &lt;code&gt;vim&lt;/code&gt; has. Here's my current .vimrc&lt;sup id="fnref3"&gt;3&lt;/sup&gt; (pathogen is only used for solarized):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execute pathogen#infect()

"--------------------------------------------------------
" general 
"--------------------------------------------------------
set nocompatible
filetype on
filetype plugin on
filetype indent on

set nobackup
set noswapfile
let mapleader = ','

set noerrorbells
autocmd! GUIEnter * set vb t_vb=

" status 
set ls=2
set statusline=%t[%{strlen(&amp;amp;fenc)?&amp;amp;fenc:'none'},%{&amp;amp;ff}]%h%m%r%y%=%c,%l/%L\ %P

" general
set background=dark
colorscheme solarized 
set tabstop=4
set shiftwidth=4
set showmatch
set number
set smarttab
set linebreak
set nowrap
set autoindent
syntax enable

" rfc
au BufRead,BufNewFile *.rfc set filetype=rfc
autocmd Filetype rfc setlocal textwidth=72 softtabstop=3 tabstop=3 shiftwidth=3

" yaml
autocmd Filetype yaml setlocal expandtab textwidth=79 softtabstop=2 tabstop=2 shiftwidth=2

" python
set expandtab
autocmd Filetype python,rst setlocal expandtab textwidth=79 softtabstop=4
"autocmd BufWritePost *.py call Flake8()

" markdown
au BufRead,BufNewFile *.md set filetype=markdown
autocmd Filetype markdown setlocal textwidth=80

" rst
au BufRead,BufNewFile *.rst set filetype=rst
autocmd Filetype rst setlocal softtabstop=3 tabstop=3 shiftwidth=3

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

&lt;/div&gt;



&lt;p&gt;The above is simple enough that I can easily set the most important settings directly within an open editor without having to open a reference guide or weed through hundreds or thousands of lines of configuration. But there's also just enough to keep me happily editing files in my primary language (Python) and make adjustments should I start writing other languages again.&lt;/p&gt;

&lt;h3&gt;
  
  
  At the end of the day
&lt;/h3&gt;

&lt;p&gt;In no way, shape or form am I saying that one shoe fits all. I don't believe that everyone should adopt my editor of choice. It's not the One True Editor (although I'll never admit that in person). Yes, there is some masochism involved. It's simply the editor that has worked best for &lt;em&gt;me&lt;/em&gt; over the years and even though I try new IDEs (PyCharm, VS Code&lt;sup id="fnref4"&gt;4&lt;/sup&gt;, Visual Studio, Emacs, etc), I &lt;em&gt;always&lt;/em&gt; find myself coming back to the warm, comforting security blanket that is &lt;code&gt;vim&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So yes, you will have to pry &lt;code&gt;vim&lt;/code&gt; from my cold, dead hands.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Of course, this assumes that the debugger is available on the target system ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Although support for this may be getting better ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Yes I know there are little niggly issues with it, so pedants please put your pitchforks away.  ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;To be fair, VS Code was the one editor that held my attention for the longest time. It's pretty damn slick. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>vim</category>
      <category>productivity</category>
      <category>distributedsystems</category>
    </item>
  </channel>
</rss>
