<?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: jess unrein</title>
    <description>The latest articles on DEV Community by jess unrein (@thejessleigh).</description>
    <link>https://dev.to/thejessleigh</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%2F102708%2Fd5d3f082-fa75-4761-844d-6cf577c5cbe3.png</url>
      <title>DEV Community: jess unrein</title>
      <link>https://dev.to/thejessleigh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thejessleigh"/>
    <language>en</language>
    <item>
      <title>Launching a django webserver in Docker</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Tue, 05 Dec 2023 22:54:14 +0000</pubDate>
      <link>https://dev.to/thejessleigh/launching-a-django-webserver-in-docker-gg2</link>
      <guid>https://dev.to/thejessleigh/launching-a-django-webserver-in-docker-gg2</guid>
      <description>&lt;p&gt;Every single time I have to set up Docker for a new web server I forget a couple key things on how to set it up. This little post is mostly a note to my future self that this is how you do it and why.&lt;/p&gt;

&lt;p&gt;For a detailed breakdown of common keywords and concepts of putting together a Dockerfile, check out my previous post on &lt;a href="https://dev.to/thejessleigh/dockerizing-a-simple-python-process-2gdk"&gt;Dockerizing a simple python process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I found myself needing to run a django server on Docker today because it turns out that you can't set memory limits using the python &lt;code&gt;resource&lt;/code&gt; library on processes on macOS since about version 10.14. Solution? Run it on a Python Docker Image that runs on Linux. Easy enough.&lt;/p&gt;

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

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;poetry
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "./myapp/manage.py", "runserver"]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Quick and dirty. I could probably be more careful with what I'm copying, where, and such. But I'm in a rush and this will do the job.&lt;/p&gt;

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

docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; myapp


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

&lt;/div&gt;

&lt;p&gt;Builds just fine, I see poetry installing the packages that it should. Beautiful!&lt;br&gt;
However, when I try to run the image...&lt;/p&gt;

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

docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 myapp
Traceback &lt;span class="o"&gt;(&lt;/span&gt;most recent call last&lt;span class="o"&gt;)&lt;/span&gt;:
  File &lt;span class="s2"&gt;"//./myapp/manage.py"&lt;/span&gt;, line 11, &lt;span class="k"&gt;in &lt;/span&gt;main
    from django.core.management import execute_from_command_line
ModuleNotFoundError: No module named &lt;span class="s1"&gt;'django'&lt;/span&gt;

The above exception was the direct cause of the following exception:

Traceback &lt;span class="o"&gt;(&lt;/span&gt;most recent call last&lt;span class="o"&gt;)&lt;/span&gt;:
  File &lt;span class="s2"&gt;"//./myapp/manage.py"&lt;/span&gt;, line 22, &lt;span class="k"&gt;in&lt;/span&gt; &amp;lt;module&amp;gt;
    main&lt;span class="o"&gt;()&lt;/span&gt;
  File &lt;span class="s2"&gt;"//./myapp/manage.py"&lt;/span&gt;, line 13, &lt;span class="k"&gt;in &lt;/span&gt;main
    raise ImportError&lt;span class="o"&gt;(&lt;/span&gt;
ImportError: Couldn&lt;span class="s1"&gt;'t import Django. Are you sure it'&lt;/span&gt;s installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?


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

&lt;/div&gt;

&lt;p&gt;That's annoying. I just saw poetry install all the libraries. And yes, I went and checked, and &lt;code&gt;django&lt;/code&gt; is present in my &lt;code&gt;poetry.lock&lt;/code&gt; and &lt;code&gt;pyproject.toml&lt;/code&gt; files. What gives? Seems like poetry is creating a virtualenv somewhere that needs to be activated, but that's really not necessary since Docker is already isolating the project and having a virtualenv is redundant. So let's try add a poetry config command to the Dockerfile.&lt;/p&gt;

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

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;poetry
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry config virtualenvs.create &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--local&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "./cohortcreation/manage.py", "runserver"]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now, the --local tag isn't strictly necessary here, because, again, Docker is isolating this project. But just in case I copy paste this somewhere where it might mess with poetry configurations, let's make sure this config value stays local to whatever project I'm working with.&lt;/p&gt;

&lt;p&gt;Next, rebuild the docker image. Loads just fine. Let's try running it.&lt;/p&gt;

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

docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 myapp
2023-12-05 22:33:32,585 INFO autoreload django.utils.autoreload Watching &lt;span class="k"&gt;for &lt;/span&gt;file changes with StatReloader
Performing system checks...

System check identified no issues &lt;span class="o"&gt;(&lt;/span&gt;0 silenced&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
December 05, 2023 - 22:33:32
Django version 4.2.4, using settings &lt;span class="s1"&gt;'myapp.project.settings'&lt;/span&gt;
Starting ASGI/Daphne version 4.0.0 development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
2023-12-05 22:33:32,806 INFO server daphne.server HTTP/2 support not enabled &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;install &lt;/span&gt;the http2 and tls Twisted extras&lt;span class="o"&gt;)&lt;/span&gt;
2023-12-05 22:33:32,806 INFO server daphne.server Configuring endpoint tcp:port&lt;span class="o"&gt;=&lt;/span&gt;8000:interface&lt;span class="o"&gt;=&lt;/span&gt;127.0.0.1
2023-12-05 22:33:32,807 INFO server daphne.server Listening on TCP address 127.0.0.1:8000


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

&lt;/div&gt;

&lt;p&gt;Hey! That looks like what I'd expect from running &lt;code&gt;./manage.py runserver&lt;/code&gt; on an average django project. Success! Now I just need to open my browser and get on with my testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqciffznluvfzt397695.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqciffznluvfzt397695.png" alt="Empty response from localhost oh no!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What? No. Why? I set up the ports when running the container. I even remembered this time to add the &lt;code&gt;-p 8000:8000&lt;/code&gt; flag to the run command the first time! I even remembered that the first port listed is for what port you want to access on the host, and the second port is the port you want Docker to forward, even though that always feels backwards to me.&lt;/p&gt;

&lt;p&gt;Okay, let's check the container and see what's up.&lt;/p&gt;

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

docker ps

&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

d8ccf5c1aa7a myapp &lt;span class="s2"&gt;"python ./myapp/ma…"&lt;/span&gt; 6 seconds ago Up 5 seconds 0.0.0.0:8000-&amp;gt;8000/tcp priceless_pascal


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

&lt;/div&gt;

&lt;p&gt;That all seems correct. Should be able to access localhost:8000 to get to whatever port 8000 on the docker container is doing. So what's happening here?&lt;/p&gt;

&lt;p&gt;I kill the container, run the webserver in a different terminal tab, just to make sure I'm not going crazy. It loads fine. I keep the local server running, but also start up the docker container again, and now I can't access the app front end through the browser anymore. So clearly docker is doing &lt;em&gt;something&lt;/em&gt; with port 8000. But letting me access my server isn't it.&lt;/p&gt;

&lt;p&gt;I'll spare you the roughly three thousand google searches I did before arriving at the answer. Long story ~short~ somewhat less long, Python thinks we're exposing our API at &lt;code&gt;127.0.0.1:8000&lt;/code&gt;. This is what we want if we're just running the server locally. &lt;em&gt;However&lt;/em&gt;, if we look at the ports output from &lt;code&gt;docker ps&lt;/code&gt;, we're not exposing &lt;code&gt;127.0.0.1:8000&lt;/code&gt;. We're exposing &lt;code&gt;0.0.0.0:8000&lt;/code&gt;. One last tweak to the Dockerfile should do the trick.&lt;/p&gt;

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

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;poetry
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry config virtualenvs.create &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--local&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "./myapp/manage.py", "runserver", "0.0.0.0:8000"]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Building the image works. Running the image works. Now I just need to cross my fingers and check my browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7opvtnyr5k8qs9vm6cp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7opvtnyr5k8qs9vm6cp.png" alt="Django Admin Panel Header"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank god. Time for a nap.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>python</category>
      <category>django</category>
      <category>poetry</category>
    </item>
    <item>
      <title>idle thought on my declining participation in dev communities</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Fri, 03 Sep 2021 21:40:56 +0000</pubDate>
      <link>https://dev.to/thejessleigh/idle-thought-on-my-declining-participation-in-dev-communities-14if</link>
      <guid>https://dev.to/thejessleigh/idle-thought-on-my-declining-participation-in-dev-communities-14if</guid>
      <description>&lt;p&gt;It’s been a good long time since I’ve posted here, attended a meet up (digital or otherwise), or engaged on dev Twitter. I was wondering why that is, and I realized that this is the happiest I’ve ever been in my job, and when that happens, my urge to do side projects and write about software dries up a bit. Not sure I have a grand thesis here. It’s great to have a job that’s fulfilling and allows work life balance in a way that I’m unused to. However, my lack of drive makes me a little sad because in the past I’ve gotten a lot of value out of my interactions with the community.&lt;/p&gt;

&lt;p&gt;Has anyone else experienced something similar? How does having a comfortable job versus an overly stressful job impact your community involvement, home life, or other behaviors? If you’re now primarily work from home and weren’t before, how does that impact where you spend your time? I’m eager to hear other perspectives. &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>worklifebalance</category>
      <category>softskills</category>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>how is everyone holding up?</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Tue, 21 Apr 2020 03:34:28 +0000</pubDate>
      <link>https://dev.to/thejessleigh/how-is-everyone-holding-up-42p2</link>
      <guid>https://dev.to/thejessleigh/how-is-everyone-holding-up-42p2</guid>
      <description>&lt;p&gt;hey folks. turns out having a soul crushing job and lack of interesting projects to work on can really decrease your blogging output. also, I had a very bad mental health 2019.&lt;/p&gt;

&lt;p&gt;i was laid off at the end of march due to covid related sales pipeline issues, leading to a lack of cash in hand, and it turns out to have been the best thing to happen for me in months. don’t get me wrong, it was incredibly nerve wracking but i didn’t realize how drained i was until I didn’t have to go in to work anymore. i start my new job next monday and i’m feeling more empowered and excited than i have in a while.&lt;/p&gt;

&lt;p&gt;one thing i wasn’t expecting about my job search is that, because i’d done so little quality coding over the last few months, i was terrified of coding exercises in the job process. my job search probably took longer than it needed to because i would just stare at those codility emails in fear, wondering how badly i was about to screw up my future. &lt;/p&gt;

&lt;p&gt;good news- once i started doing them it jumpstarted my brain and i was like  “oh, i know how to do this. this isn’t so scary.” so for anyone out there who is also job searching amid the layoffs and procrastinating due to fear of failure, know 1. you’re not alone and 2. it’s unlikely to be as bad as you think. the waiting is the hardest part. &lt;/p&gt;

&lt;p&gt;anyhow, that was a whole lot of rambling but i missed Dev and the community here, so i thought i’d dip my toe back in and see what’s up. what’s changed since i’ve been on hiatus? how are you holding up through everything happening with covid? come say hi in the comments. we’re all in this together. &lt;/p&gt;

</description>
      <category>personal</category>
      <category>chat</category>
      <category>discuss</category>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>programming languages as crafts and toys</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Sat, 27 Jul 2019 17:31:50 +0000</pubDate>
      <link>https://dev.to/thejessleigh/programming-languages-as-crafts-and-toys-53dm</link>
      <guid>https://dev.to/thejessleigh/programming-languages-as-crafts-and-toys-53dm</guid>
      <description>&lt;p&gt;Java: LEGO&lt;br&gt;
Python: Knitting&lt;br&gt;
Ruby: Crochet&lt;br&gt;
JavaScript: Cross stitch&lt;br&gt;
HTML/CSS: Sand Art&lt;br&gt;
Web Assembly: Embroidery&lt;br&gt;
Go: K’nex&lt;br&gt;
SQL: Silly Putty&lt;br&gt;
PHP: Pony Bead Melting&lt;br&gt;
C: Glassblowing&lt;br&gt;
COBOL: blacksmithing&lt;/p&gt;

&lt;p&gt;I will not be explaining further or taking questions. It’s not rational. This is just something I feel deeply in my soul.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>A PSA for Well Meaning Coworkers This International Women's Day</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Fri, 08 Mar 2019 16:42:41 +0000</pubDate>
      <link>https://dev.to/thejessleigh/a-psa-for-well-meaning-coworkers-this-international-womens-day-10e8</link>
      <guid>https://dev.to/thejessleigh/a-psa-for-well-meaning-coworkers-this-international-womens-day-10e8</guid>
      <description>&lt;p&gt;I usually try not to post twice in one day, but this is timely. My standup this morning started with the following exchange.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dude&lt;/strong&gt;: Oh, yeah, Carol is at that women's thing. Jess, why aren't you there? What do you have against women?&lt;br&gt;
&lt;strong&gt;Me&lt;/strong&gt;: Well, I'm not a woman.&lt;br&gt;
&lt;strong&gt;(group laughter)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Me&lt;/strong&gt;: Yeah. I'm nonbinary.&lt;br&gt;
(group silence)&lt;br&gt;
&lt;strong&gt;Dude 2&lt;/strong&gt;: I.....didn't know.&lt;/p&gt;

&lt;p&gt;Now, I'm not militant at work about enforcing correct pronoun usage, and I don't remind people that I'm nonbinary all the time. But I do have it in my public profile at work and on slack, and it stings when the people around me put in &lt;em&gt;so little effort&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But even if I weren't nonbinary, there are a few pieces of advice I'd like to give.&lt;/p&gt;

&lt;p&gt;1.) Don't ask women why they are or aren't participating in a woman focused event. Not all women focused events are safe for every woman, and not every woman wants to center her career on her gender. This is a rude and uncomfortable question.&lt;/p&gt;

&lt;p&gt;2.) Don't ask women to explain why women-centric spaces are necessary, or why they're okay when a "men's group" would be given side-eye. That's a question you can google for, or you can reach out to group organizers in private if you have earnest follow up questions.&lt;/p&gt;

&lt;p&gt;3.) Don't ask women or nonbinary people what they have against women or why they hate women, even in jest. Don't make jokes at your coworkers' expense that are designed to make them defensive or feel bad. That's a shitty way to be a coworker, an ally, or a friend.&lt;/p&gt;

&lt;p&gt;This has been a public service announcement&lt;/p&gt;

&lt;p&gt;###&lt;/p&gt;

</description>
      <category>wecoded</category>
      <category>theycoded</category>
      <category>psa</category>
      <category>venting</category>
    </item>
    <item>
      <title>Driven mostly by spite, jess coded</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Fri, 08 Mar 2019 16:09:34 +0000</pubDate>
      <link>https://dev.to/thejessleigh/driven-mostly-by-spite-jess-coded--hij</link>
      <guid>https://dev.to/thejessleigh/driven-mostly-by-spite-jess-coded--hij</guid>
      <description>&lt;h2&gt;
  
  
  I continued to code in 2019 because...
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Coding for a living is like getting to solve puzzles for a living. I love that.&lt;/li&gt;
&lt;li&gt;My first VP of Engineering told after only three months on the job that I just didn't seem "technical enough," and that I should consider a "people management" or product manager track instead. I hope he sees my entire career as a "fuck you" direct from me to him.&lt;/li&gt;
&lt;li&gt;Gotta keep a roof over my head.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I deserve credit for...
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prioritizing my health and work life balance over the last 14 months. It's easy to fall into a rut, especially at a desk job where you don't have to interact with people as much as many other professionals do. &lt;/li&gt;
&lt;li&gt;Taking time for myself. &lt;/li&gt;
&lt;li&gt;Coming out as nonbinary at work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I hope to see my tech community...
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Start treating nonbinary people as legitimate, rather than "woman-lite." &lt;/li&gt;
&lt;li&gt;Start including more WOC and nonbinary POC voices in leadership. &lt;/li&gt;
&lt;li&gt;Commit to pay transparency.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>theycoded</category>
      <category>vecoded</category>
    </item>
    <item>
      <title>3 Tricky Python Nuances</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Wed, 16 Jan 2019 20:57:47 +0000</pubDate>
      <link>https://dev.to/thejessleigh/three-python-nuances-i-wish-id-known-earlier-547c</link>
      <guid>https://dev.to/thejessleigh/three-python-nuances-i-wish-id-known-earlier-547c</guid>
      <description>&lt;p&gt;This blog post is adapted from a talk I gave last week at &lt;a href="https://chipy.org"&gt;ChiPy&lt;/a&gt;, the Chicago Python User Group. For more great content like this, I highly recommend your local Python meetup, and &lt;a href="https://pyvideo.org"&gt;PyVideo&lt;/a&gt;, the community project aimed at indexing every recorded Python meetup or conference talk.&lt;/p&gt;

&lt;h1&gt;
  
  
  Nuance Number One
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Lists Don't Contain Objects
&lt;/h2&gt;

&lt;p&gt;I know this sounds like a ridiculous statement. Everything in Python is an object. If declare a list, I'm filling it full of objects. Collecting objects is what a list does!&lt;/p&gt;

&lt;p&gt;Kind of. Let me back up a bit.&lt;/p&gt;

&lt;p&gt;Over &lt;a href="https://adventofcode.com"&gt;Advent of Code&lt;/a&gt; a couple friends and I were sharing code snippets and debugging things together in our Friendship Discord. One of the problems involved creating a giant matrix that represented cloth, and you had to figure out which squares of cloth were being used by manipulating individual elements in that matrix. I have a friend who is learning Python right now, and setting the problem up turned out to be &lt;a href="https://dev.to/rsdesoto/til-python-comprehensions-make-matrices-work-1ge1"&gt;more difficult than they thought&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's how they set up their matrix initially. Simple enough, right?&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;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&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;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good. But when they went to change an element in the first nested list...&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;l&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;😧&lt;/p&gt;

&lt;p&gt;They posted in our discord group, asking what the heck was going on. My answer, as usual, was pretty much to shrug and ask why they weren't using a list comprehension. Another of our friends said something to the effect of "seems like it could be pointers?"&lt;/p&gt;

&lt;p&gt;My second friend was right. Python doesn't store actual objects inside a list. It stores &lt;code&gt;references&lt;/code&gt; to objects. And it's hella weird.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;reference&lt;/code&gt; points to a specific location in memory. That specific location contains the object, and if you change the object stored at that location in memory, you change every instance where it appears.&lt;/p&gt;

&lt;p&gt;So each of the five lists inside the original matrix declared by &lt;code&gt;l = [[0] * 5] * 5&lt;/code&gt; are actually just five separate &lt;code&gt;references&lt;/code&gt; to the &lt;em&gt;same&lt;/em&gt; list. When you change one, you change them all.&lt;/p&gt;

&lt;h3&gt;
  
  
  So wait, how do I make them different objects?
&lt;/h3&gt;

&lt;p&gt;Great question! In order to accomplish the goal of having a matrix where each element can be manipulated independently without inadvertently affecting other elements, we need to make sure we're creating a new object at a new location in memory for each nested list. In this case, we can use the copy library to create a new container object that we can change independently.&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;from&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="mi"&gt;0&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="c1"&gt;# intentionally verbose example to illustrate a point :)
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But of course, we can make this much cleaner. I was right the first time. Mooooooost of your Python problems can be solved with a well placed list comprehension (satisfaction not guaranteed).&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;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;l&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Nuance Number Two
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Default arguments are evaluated when the function is defined, &lt;em&gt;not&lt;/em&gt; each time a function is called
&lt;/h2&gt;

&lt;p&gt;What does this mean? It's easier to explain through an example. Let's pretend that, for some reason, we want to write a function that takes in an element and appends it to an existing list without using the &lt;code&gt;append&lt;/code&gt; function.&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;list_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
    &lt;span class="n"&gt;input_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input_list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this is a completely contrived example that we would never implement in practice, but it's useful for demonstration. Let's try calling that function a few times.&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list_append&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="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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list_append&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="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&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="mi"&gt;7&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="nf"&gt;list_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asdf&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="mi"&gt;3&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="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asdf&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;When we call the function multiple times, it seems that it's always adding to and returning the same object, rather than starting with a fresh empty list each time the function is called.&lt;/p&gt;

&lt;p&gt;This is because the default argument for a function is created and evaluated when the function is first declared. So it would get evaluated and you'd start with a fresh list every time you restarted your app, for example. But each time your function is called while the app is running it's going to be working off of the same mutable object, and will just keep growing and growing until the app is restarted again.&lt;/p&gt;

&lt;p&gt;Here's how we fix 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;list_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;input_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;input_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;element&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="nf"&gt;list_append&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="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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list_append&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="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&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="nf"&gt;list_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asdf&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;bcde&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;bcde&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;asdf&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;Elements declared within a function's scope are evaluated every time the function runs, so in the above example you'll be starting with a new empty list every single time, instead of passing the same default argument from function call to function call.&lt;/p&gt;

&lt;p&gt;This isn't just a problem with lists, by the way. You should always be careful about passing in mutable objects as default arguments to a function; all mutable objects will have this same problem.&lt;/p&gt;

&lt;h1&gt;
  
  
  Nuance Number Three
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;==&lt;/code&gt; vs &lt;code&gt;is&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;is&lt;/code&gt; and &lt;code&gt;==&lt;/code&gt; are both operators in Python that evaluate two objects against one another and return a boolean value based on the results. This is a little tricky, but &lt;code&gt;==&lt;/code&gt; checks for &lt;em&gt;equality&lt;/em&gt; while &lt;code&gt;is&lt;/code&gt; checks for &lt;em&gt;identity&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Remember back to nuance number one when we talked about &lt;code&gt;references&lt;/code&gt;, or specific locations in memory that contain an object? They're important again here!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Equality&lt;/em&gt; means that the values of two objects match. &lt;em&gt;Identity&lt;/em&gt; means that the two things you're comparing are the &lt;em&gt;exact same object&lt;/em&gt; and exist at the same location in memory. You can check the location of an object in memory by using the builtin Python function &lt;code&gt;id()&lt;/code&gt;. So when you compare two objects using the &lt;code&gt;is&lt;/code&gt; operator, you're checking that the two objects have the same &lt;code&gt;id&lt;/code&gt;, not the same &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A quirk of this, that you'll often see in ideomatic Python, is that there is only one &lt;code&gt;True&lt;/code&gt; boolean object and only one &lt;code&gt;False&lt;/code&gt; boolean object in memory. Each appearance of &lt;code&gt;True&lt;/code&gt; has the same id as every other appearance of &lt;code&gt;True&lt;/code&gt;. However, there are some values that evaluate as being &lt;em&gt;equal&lt;/em&gt; to &lt;code&gt;True&lt;/code&gt; without sharing its identity. That's why Pythonistas often use the &lt;code&gt;is&lt;/code&gt; operator when comparing things to &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt; rather than the &lt;code&gt;==&lt;/code&gt; operator.&lt;/p&gt;

&lt;p&gt;This can be a bit confusing and jargon filled when explained in words. Here are some examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Equality vs identity for boolean values:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&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="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="nf"&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="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&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;a&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&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;b&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Equality vs identity - equivalent variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# a and b variables point to the same id
&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&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="mi"&gt;2&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="mi"&gt;4&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&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;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&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;a&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="mi"&gt;4437740104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4437740104&lt;/span&gt;

&lt;span class="c1"&gt;# a and b are different container objects, but the values of their contents are identical
&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&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="mi"&gt;2&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="mi"&gt;4&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;b&lt;/span&gt; &lt;span class="o"&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="mi"&gt;2&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="mi"&gt;4&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&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;a&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="mi"&gt;4437740104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4442640968&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I'm not sure I have a unified conclusion here. These are just three things that might trip you up in Python that are hard to google for, since they lead to unexpected behaviors but don't give you a nice error message.&lt;/p&gt;

&lt;p&gt;I suppose if I were to give you a takeaway it's that&lt;br&gt;
1.) &lt;code&gt;references&lt;/code&gt; are kind of wonky. You usually don't have to think about them, but they're worth understanding&lt;br&gt;
2.) Always use list comprehensions&lt;/p&gt;

&lt;p&gt;As always, let me know if you have any questions in the comments, or if you think I missed anything important regarding these three topics. Let me know if there's a Python quirk you don't get and would like an explainer on. Thanks!&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>explainlikeimfive</category>
    </item>
    <item>
      <title>What Even Is Data Engineering?</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Wed, 12 Dec 2018 16:25:42 +0000</pubDate>
      <link>https://dev.to/thejessleigh/what-even-is-data-engineering-3g5m</link>
      <guid>https://dev.to/thejessleigh/what-even-is-data-engineering-3g5m</guid>
      <description>&lt;p&gt;I started my first job as a Data Engineer back in September. I wasn't exactly sure what the job entailed, but I was ready to shift my focus and learn new things. I'd spent the previous four years on back end application development, and while I learned a lot, I was ready to shift away from primarily consuming and constructing APIs.&lt;/p&gt;

&lt;p&gt;When I was interviewing for the position, I read up on DAGs, ETL pipelines, and specific common database patterns like the &lt;a href="https://en.wikipedia.org/wiki/Star_schema"&gt;star schema&lt;/a&gt;. However, in my interview I was asked things "how do you optimize this SQL query" and "assuming you don't have an automated load balancer in place, how do you decide which cluster to create a new database in?" They were interesting questions, so I decided to take the job. But it can be difficult to explain, even to other engineers, what the scope of my job is.&lt;/p&gt;

&lt;p&gt;My team is responsible for database infrastructure. We make sure that database clusters aren't overloaded. We're concerned with internal data security. We manage database users and permissions, and field requests from different teams. We're the information gathering arm for the Business Intelligence team; if they need a dump of data from a specific API every 24 hours, we write the ETL pipeline to do it, and manage the data warehouse where all that information lives. My job is kind of a grab bag of things that need to get done but might not have an obvious owner.&lt;/p&gt;

&lt;p&gt;I know I probably haven't done a great job of answering the question "What even &lt;em&gt;is&lt;/em&gt; data engineering?" because I'm not exactly sure I know myself! I recently met up with &lt;a href="https://dev.to/alysivji"&gt;Ali&lt;/a&gt; at a local Python meetup and we bonded over the fact that the discipline of data engineering seems utterly made up.&lt;/p&gt;

&lt;p&gt;In this series, however, I'm going to introduce different tools that I use in my work through the lens of data engineering. If you have specific questions, or want to know about specific tools commonly used by data engineers, let me know in the comments. Data engineering is a fairly new field in software development, and the boundaries are still being drawn. Let's suss it out together!&lt;/p&gt;

</description>
      <category>data</category>
      <category>businessintelligence</category>
      <category>engineering</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Where do you find conference CFPs?</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Mon, 10 Dec 2018 21:42:34 +0000</pubDate>
      <link>https://dev.to/thejessleigh/where-do-you-find-conference-cfps-1imd</link>
      <guid>https://dev.to/thejessleigh/where-do-you-find-conference-cfps-1imd</guid>
      <description>&lt;p&gt;I did quite a bit of conference and meetup speaking in 2016 and 2017, but decided to take 2018 off of speaking due to some burnout and health concerns. But now I'm back! But the landscape seems to be totally different from the last time I was CFP hunting.&lt;/p&gt;

&lt;p&gt;Lanyrd is permanently down, the Technically Speaking newsletter is now defunct, and Papercall.io continues to be an unhelpful mess.&lt;/p&gt;

&lt;p&gt;For those of you who do conference speaking, which channels do you keep an eye on for finding new CFPs?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>speaking</category>
      <category>conferences</category>
      <category>advice</category>
    </item>
    <item>
      <title>Different types of testing explained</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Thu, 06 Dec 2018 21:47:54 +0000</pubDate>
      <link>https://dev.to/thejessleigh/different-types-of-testing-explained-1ljo</link>
      <guid>https://dev.to/thejessleigh/different-types-of-testing-explained-1ljo</guid>
      <description>&lt;p&gt;In standup the other day, my team's DBA was talking about running smoke tests for his most recent project. I've heard people talk about smoke tests before, but for some reason it never really clicked that I have no idea what a smoke test is. How is it different than a unit test? An integration test? A regression test?&lt;/p&gt;

&lt;p&gt;It feels a little embarrassing at this point that I can't articulate the difference between these things, so I decided to do a little research and write up an explainer so that I can reference it in the future and not feel like an ignorant dingus. I figured, since I've been working as a dev for almost 5 years and had this question, there are probably others out there who are similarly too shy to ask.&lt;/p&gt;

&lt;p&gt;After reading a bunch of different blog posts, stack overflow questions, and random resources I've constructed a Frankenstein approximation of a consensus for several different categories of tests. After a little bit of time spent googling, I think there are three good things to think about to understand different kinds of testing.&lt;/p&gt;

&lt;p&gt;1.) What kind of thing do they test?&lt;br&gt;
2.) When are these tests written and run?&lt;br&gt;
3.) What information does a test failure provide?&lt;/p&gt;

&lt;p&gt;Different people have different definitions, and a single test suite might include multiple types of tests. For example, you might have a set of tests you run that combine integration tests and regression tests into a single suite. That's fine. There are grey areas, and teams have a habit of developing their own, team-specific vocabulary. You don't need to have a comprehensive suite for each of these categories. You should test at the level that makes sense for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the complexity of your app&lt;/li&gt;
&lt;li&gt;the amount of traffic your app sees&lt;/li&gt;
&lt;li&gt;the size of your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you think I've radically mischaracterized or omitted something important, especially if you work in testing, please let me know in the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit tests
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What do they test?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unit tests evaluate that each atomic unit of code performs the way it's supposed to. Ideally, when you're planning and writing unit tests, you should isolate functionality that can't be broken down any further, and then test that.&lt;/p&gt;

&lt;p&gt;Unit tests should &lt;em&gt;not&lt;/em&gt; test external dependencies or interactions. You should definitely mock out api calls. Unit test purists would also have you mock out database calls and only ensure that your code operates correctly given correct inputs from outside sources. Depending on your existing codebase or your manager's preferences, this might not be possible. If you aren't able to exclude database functionality from your unit test suite, make sure you are mindful of performance and look for &lt;a href="https://dev.to/thejessleigh/speed-up-your-postgresql-unit-tests-with-one-weird-trick-364p"&gt;potential optimizations&lt;/a&gt;. I can tell you from experience that long running unit test suites are extremely unpleasant and slow down development significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I run them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should write and run unit tests in parallel with your code. When people refer to Test Driven Development, they're referring to unit tests, and using the tests as the spec for what your code should accomplish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when they fail?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A failing unit test lets you know that a specific piece of code is busted. If you've broken it down far enough, your failure should zoom in on the exact piece of code that isn't working as intended.&lt;/p&gt;

&lt;p&gt;Failures should help you identify and fix problems quickly, and let you know when your specs need to be updated. They're probably a good guide for when to update your code documentation as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration tests
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What do they test?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Integration tests check the interaction between two or more atomic units of code. Your application is composed of individual units that perform specific small functions, and each of those small functions might work in isolation but break when you knit them together.&lt;/p&gt;

&lt;p&gt;Integration tests also test the integration of your code with outside dependencies, like database connections or third party APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I run them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Integration tests should be the next step after unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when they fail?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When an integration test fails, it tells you that two or more core functions of your application aren't working together. These might be two modules you've written that clash in some complicated business logic, or a failure resulting from a third party API changing the structure of their response. It might alert you to bad error handling in the case of a database connection failure.&lt;/p&gt;

&lt;p&gt;Failures might be easy to identify, or they might require some manual validation and experimentation to identify. Difficult to solve integration test failures are an indication of where you can improve your logging and error handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regression testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What do they test?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Regression tests check a set of scenarios that worked in the past and should be relatively stable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I run them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should run your regression tests after your integration tests pass. Do not add your new feature to the regression test suite until existing regression tests pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when they fail?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A regression test failure means that new functionality has broken some existing functionality, causing a regression. &lt;/p&gt;

&lt;p&gt;The failure should let you know what old capabilities are broken, and indicate that you need to write additional integration tests between your new feature and the old, broken feature.&lt;/p&gt;

&lt;p&gt;A regression test failure might also indicate that you have inadvertently reintroduced a bug that you fixed in the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smoke testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What do they test?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smoke tests are a high level, tightly curated set of automated tests that live somewhere in the space between integration and regression tests. They're there as a sanity check that your site's core functionality isn't wrecked.&lt;/p&gt;

&lt;p&gt;The term &lt;code&gt;smoke test&lt;/code&gt; seems to be a holdover from plumbing. If you could see smoke or steam coming out of a pipe, it was leaky and needed to be fixed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I run them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smoke tests should be a test of your whole system together, ensuring that core functionality remains intact. These shouldn't be comprehensive. These are your significant, big picture, no-go test failures. You should run them early and often, ideally daily, in both staging and production environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when they fail?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a smoke test fails there's a significant problem with your site's functionality. You should not deploy the new changes until these failures are addressed. If they fail in production, fixing these should be very high priority. &lt;/p&gt;

&lt;h2&gt;
  
  
  Acceptance testing
&lt;/h2&gt;

&lt;p&gt;(I've also heard this called QA/BV/Manual testing, etc.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do they test?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Acceptance testing is usually a set of manual tests performed after the end-to-end development is finished. They check to make sure that the feature as written actually meets all of the initial specifications, or acceptance criteria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when they fail?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Looks like you missed a bit of functionality when writing your code. You'll need to go back to development and fix that. :(&lt;/p&gt;

&lt;p&gt;If acceptance tests fail you probably need to decide on acceptance criteria earlier in your planning process next time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I run them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since these are manual tests, not tests run as code, the timing is a little different. You and your project owner should draft a set of acceptance criteria before work begins on a project. Any additional scope that's discovered or added to the project should be reflected in the acceptance criteria.&lt;/p&gt;

&lt;p&gt;Acceptance tests should happen fairly quickly after development is complete so that you can go back and iterate quickly if something isn't quite right. It makes sense to do these right after unit or integration testing, before you've gone too far in the testing process before significant changes need to be made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What do they test?&lt;/strong&gt;&lt;br&gt;
Performance tests check stability, scalability, and usability of your product and infrastructure. You might check things like number of errors per second or how long it takes to load a page. There isn't necessarily pass/fail criteria associated with a performance test. This stage is more about data gathering and looking for areas of improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when they fail?&lt;/strong&gt;&lt;br&gt;
Performance tests don't exactly fail in the same way that a unit test suite would fail. Instead you collect a set of benchmarks and assess them against where you want those numbers to be. If your performance test fails, it might tell you that you need to pay more attention to infrastructure scaling, database query time, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I run them?&lt;/strong&gt;&lt;br&gt;
Performance tests are a good idea after major releases and refactors. &lt;/p&gt;

&lt;h2&gt;
  
  
  Load testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What do they test?&lt;/strong&gt;&lt;br&gt;
Load testing is a kind of specialized performance test that specifically checks how your product performs under significant stress over a predetermined period of time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when they fail?&lt;/strong&gt;&lt;br&gt;
Load tests assess how prepared you are for a significant increase in traffic. If a load test fails, it doesn't mean that your site is broken, but it does mean that you aren't prepared for a viral hit or a DDOS attack. This is probably not a big deal for small products just starting out, but failure should be a concern as your userbase starts to scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When do I write them?&lt;/strong&gt;&lt;br&gt;
Load tests should not be your first concern right out of the gate, but as your product becomes bigger and more established, you should probably run load tests on new features to see if they will affect the overall performance of the site and see if they can be optimized.&lt;/p&gt;

&lt;p&gt;I can no longer say I don't know what a smoke test is, and hopefully you learned something along the way too! As I mentioned above, I am not a tester, so if you notice something I've missed or misinterpreted, let me know in the comments! &lt;/p&gt;

&lt;p&gt;:)&lt;/p&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>explainlikeimfive</category>
    </item>
    <item>
      <title>Papers We Love: Exploring Gamification Among Elderly Persons</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Mon, 03 Dec 2018 17:08:50 +0000</pubDate>
      <link>https://dev.to/thejessleigh/papers-we-love-exploring-gamification-among-elderly-persons-31c6</link>
      <guid>https://dev.to/thejessleigh/papers-we-love-exploring-gamification-among-elderly-persons-31c6</guid>
      <description>&lt;p&gt;Welcome back to Papers We Love! Sorry for the absence last week - the holidays kind of took the pepper out of me and I fell behind. BUT I picked out a paper on &lt;a href="http://gamification-research.org/wp-content/uploads/2011/04/12-Gerling.pdf" rel="noopener noreferrer"&gt;gamification among frail elderly patients&lt;/a&gt; that I think will lead to a really good discussion (and isn't too long of a read :) ).&lt;/p&gt;

&lt;p&gt;I picked this paper somewhat selfishly. My dad worked in geriatric practice when I was growing up. As a result, I spent a lot of time tagging around with him to nursing homes and interacting with patients. I'm very interested in the intersection of elder care, healthcare, and technology. It's almost like this paper was tailor made for me.&lt;/p&gt;

&lt;p&gt;I think the paper did a great job of outlining possible gains as well as technical, cultural, and accessibility challenges. However, I was a bit surprised and dismayed that the paper didn't touch on possible ethical and privacy ramifications of this kind of data gathering. Sure, you might be able to observe a decline and medically intervene earlier. But how is this information being collected and stored? Who has access, and is participation voluntary for the patient? Will this integrate with an Electronic Medical Record, and will performance in gamified activities impact insurance rates or treatment options?&lt;/p&gt;

&lt;p&gt;I am incredibly wary of policies and technologies that treat elderly patients as having fewer rights than other people. It's a trap that can rob patients of their dignity at the same time they're being robbed of their mobility, senses, and ability to live autonomously, and I think it's imperative to keep these questions in mind when integrating technology with elder care.&lt;/p&gt;

</description>
      <category>pwl</category>
      <category>discuss</category>
      <category>gamification</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Dockerizing a Simple Python Process</title>
      <dc:creator>jess unrein</dc:creator>
      <pubDate>Fri, 16 Nov 2018 16:14:49 +0000</pubDate>
      <link>https://dev.to/thejessleigh/dockerizing-a-simple-python-process-2gdk</link>
      <guid>https://dev.to/thejessleigh/dockerizing-a-simple-python-process-2gdk</guid>
      <description>&lt;p&gt;This is part two in a series on taking a simple Python project from local script to production. In &lt;a href="https://dev.to/thejessleigh/converting-a-csv-writer-from-python-2-to-python-3-1cnb"&gt;part one&lt;/a&gt; I talked about a gotcha I ran into when converting an old project from Python 2 to Python 3.&lt;/p&gt;

&lt;p&gt;This part will go over how I put my Python process, its inputs, and its outputs into a Docker container and made an image publicly available on &lt;a href="https://dockerhub.com" rel="noopener noreferrer"&gt;Dockerhub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Requirements that I will not go over here. Go to Docker.com and follow the instructions there&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download docker&lt;/li&gt;
&lt;li&gt;Create a docker id&lt;/li&gt;
&lt;li&gt;Log in with your docker id on Dockerhub&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What is Docker?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docker.com" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; is a containerization platform. Containerization is a way to package units of code with their dependencies so that they have everything they need to run in isolation.&lt;/p&gt;

&lt;p&gt;Using Docker can help fix the "it works on my machine" problem, and writing dockerized code is a great way to encourage thoughtful code practices. Docker containers should be simple, responsible for as little as possible, and dependent on as few externals as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker image vs docker container
&lt;/h2&gt;

&lt;p&gt;Throughout this post, and online, you'll see the terms &lt;code&gt;container&lt;/code&gt; and &lt;code&gt;image&lt;/code&gt;. An &lt;code&gt;image&lt;/code&gt; is basically a snapshot of your dockerized code that is created when you use the &lt;code&gt;docker build&lt;/code&gt; command - more on that below. Docker images start a container when you use &lt;code&gt;docker run&lt;/code&gt; on that image. So a &lt;code&gt;container&lt;/code&gt; is a running instance of an &lt;code&gt;image&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Anatomy of a Dockerfile
&lt;/h1&gt;

&lt;p&gt;I decided to dockerize my csv writer from &lt;a href="https://dev.to/thejessleigh/converting-a-csv-writer-from-python-2-to-python-3-1cnb"&gt;the previous post in this series&lt;/a&gt; so that I could move it between environments easily.&lt;/p&gt;

&lt;p&gt;For this I needed a Dockerfile. A Dockerfile is a text file that does not have a file extension.&lt;/p&gt;

&lt;p&gt;Here's what the dockerfile for my Python code looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.7
ARG export_file=goodreads.csv
COPY $export_file goodreads_export.csv
COPY converter.py /
CMD ["python", "./converter.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  FROM
&lt;/h2&gt;

&lt;p&gt;The FROM keyword here indicates a dependency. Docker containers don't have languages automatically loaded. To access Python to run the code, we need to instruct the image to include &lt;code&gt;python:3.7&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A note on Docker registries:&lt;br&gt;
the default Docker registry is &lt;a href="https://dockerhub.com" rel="noopener noreferrer"&gt;Dockerhub&lt;/a&gt;. If a docker image is available on Dockerhub, you don't need to specify a url when pulling or pushing from a docker repo. You just need the author's username and the repo name. For example, you can pull the docker image from this post with the command &lt;code&gt;docker pull thejessleigh/goodreads-libib-converter&lt;/code&gt;. If you're using a different registry you'll need to tell Docker where to go. For example, if you're using Quay you'd do &lt;code&gt;docker pull quay.io/example-username/test-docker-repo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The python dependency in my Dockerfile doesn't have a username because it's an &lt;strong&gt;official&lt;/strong&gt; repo hosted on Dockerhub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ARG
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ARG&lt;/code&gt; declares an argument. It is the only instruction in a Dockerfile that can precede &lt;code&gt;FROM&lt;/code&gt;, although I prefer to have &lt;code&gt;FROM&lt;/code&gt; come first for the sake of consistency.&lt;/p&gt;

&lt;p&gt;In the above example, I declare an &lt;code&gt;ARG&lt;/code&gt; &lt;code&gt;export_file&lt;/code&gt; and give it a default. It expects a file called &lt;code&gt;goodreads.csv&lt;/code&gt; in the same directory as the Dockerfile. If I want to pass in something different, I instruct it to use a different filename with &lt;code&gt;--build-arg=export_file=my_goodreads_export.csv&lt;/code&gt; when building the image.&lt;/p&gt;

&lt;h2&gt;
  
  
  COPY
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;ADD&lt;/code&gt; duplicate the contents of a file into the docker image. This is where I'm importing the input file and also the actual Python code that the Docker image executes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY&lt;/code&gt; takes two arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the location of the file you're putting into the image&lt;/li&gt;
&lt;li&gt;the location of the file &lt;em&gt;inside&lt;/em&gt; the docker image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So whatever file I include as the CSV to convert will be referred to as &lt;code&gt;goodreads_export.csv&lt;/code&gt; inside the Docker container. This is nifty, because it means that no matter what I build the docker image with, the filename will always be consistent. I don't have to worry about making the Python code handle different filenames or paths. It can always look for &lt;code&gt;./goodreads_export.csv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are some subtle differences between &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;ADD&lt;/code&gt; that &lt;a href="https://dev.to/ryanwhocodes"&gt;@ryanwhocodes&lt;/a&gt; has already written about, so I'll leave his post &lt;a href="https://web.archive.org/web/20181023142837/https://dev.to/ryanwhocodes/dockerfile-copy-vs-add-key-differences-and-best-practices-4191"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Update September 2019: It appears that this post is no longer available on dev.to so I have replaced the embedded post with an archive.org link.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  RUN
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt; issues an instruction that is executed and committed as part of the image. If I were dockerizing a Python project that needed to install external packages, I could use &lt;code&gt;RUN&lt;/code&gt; to &lt;code&gt;pip install&lt;/code&gt; those dependencies. However, &lt;code&gt;converter.py&lt;/code&gt; is a very simple process that doesn't need external packages, so I don't need to run anything as part of my build process.&lt;/p&gt;

&lt;h2&gt;
  
  
  CMD
&lt;/h2&gt;

&lt;p&gt;There can only be one &lt;code&gt;CMD&lt;/code&gt; instruction per Dockerfile. If the Dockerfile contains multiple &lt;code&gt;CMD&lt;/code&gt;s, only the last one will execute.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; is the command you intend the image to do when you run an instance of it as a container. It is not executed as part of the build process for an image. &lt;code&gt;CMD&lt;/code&gt; is different from &lt;code&gt;RUN&lt;/code&gt; in this way.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building a docker image
&lt;/h1&gt;

&lt;p&gt;Now we have everything necessary to build a Docker image for our Python code from the Dockerfile.&lt;/p&gt;

&lt;p&gt;As stated above, a Docker &lt;code&gt;image&lt;/code&gt; is an inert snapshot of an environment that is ready to execute a command or program, but &lt;em&gt;has not yet&lt;/em&gt; executed that command.&lt;/p&gt;

&lt;p&gt;To build using the above Dockerfile, we run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build --build-arg=export_file=goodreads_export.csv -t goodreads-libib-converter .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--build-arg&lt;/code&gt; tells Docker to build the image with a file called &lt;code&gt;goodreads_export.csv&lt;/code&gt;, overriding the default expectation of &lt;code&gt;goodreads.csv&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;-t goodreads-libib-converter&lt;/code&gt; "tags" the image as &lt;code&gt;goodreads-libib-converter&lt;/code&gt;. This is how you create your container with a human readable &lt;code&gt;REPOSITORY&lt;/code&gt; name.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.&lt;/code&gt; tells Docker to look for a Dockerfile to build in the current directory.&lt;/p&gt;

&lt;p&gt;After I do this, I can see that the image was successfully created by checking my image list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; docker image list
REPOSITORY                 TAG       IMAGE ID       CREATED             SIZE
goodreads-libib-converter  latest    1234567890     12 seconds ago      924MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Running a Docker container
&lt;/h1&gt;

&lt;p&gt;Now that I have an &lt;code&gt;image&lt;/code&gt;, I have a standalone environment capable of running my program, but it hasn't actually executed the core procedure specified with &lt;code&gt;CMD&lt;/code&gt; yet. Here's how I do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run goodreads-libib-converter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I see the print debugging statements I have in my &lt;code&gt;converter.py&lt;/code&gt; file execute, so I know how many CSV rows are being converted. When I ran the program locally, it created an output file called &lt;code&gt;libib_export.csv&lt;/code&gt;. However, when I check the contents of my directory now, it's not there. How is that useful!?&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing Files Written Out
&lt;/h2&gt;

&lt;p&gt;I'm no longer running the Python code in the directory I was before. I'm running it &lt;em&gt;inside&lt;/em&gt; the Docker container. Therefore, any files that are written out will also be stored &lt;em&gt;inside&lt;/em&gt; the Docker container. The output file doesn't do me much good in there!&lt;/p&gt;

&lt;p&gt;I'm running the Docker container locally, so all I have to do is find the container and copy the output file from it's dockerized location to the place I actually want it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker cp container_id:/libib_export.csv ~/outputs/libib_export.csv

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

&lt;/div&gt;



&lt;p&gt;This extracts the resultant CSV output from &lt;code&gt;converter.py&lt;/code&gt; and puts it somewhere I can access it.&lt;/p&gt;

&lt;p&gt;I can figure out the &lt;code&gt;container_id&lt;/code&gt; (or the human readable name) with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; docker ps -a
CONTAINER ID  IMAGE                   COMMAND                  CREATED             NAMES
e00000000000  goodreads-libib-export  "python ./converter.…"   24 seconds ago      naughty_mcclintock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Yes, naughty_mcclintock is actually the procedurally generated name for the container I've been working with locally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Copying a file from a container to my desired location is fine for a local environment, but has limited uses if I ever want to take this project to production. There are other, better options for dealing with output files from Docker containers, but we'll get into that ✨ in another installment in this series ✨&lt;/p&gt;

&lt;h1&gt;
  
  
  Committing a docker image
&lt;/h1&gt;

&lt;p&gt;After we've run the container to confirm that it works, we probably to create a new image based on the changes it made when it executed. We're preparing the image that we want to push up into an external Docker registry, like Dockerhub.&lt;/p&gt;

&lt;p&gt;When committing a Docker image, we need to specify the registry (if it's something other than dockerhub), the author name, the repository name, and the tag name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker commit -m "Working Python 3 image" naughty_mcclintock thejessleigh/goodreads-libib-converter:python3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My &lt;code&gt;docker commit&lt;/code&gt; was successful, so I see a sha256 hash output in my terminal. Creating a commit message is, of course, optional. But I like to do it to keep organized.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A note on Docker image tags:&lt;br&gt;
When you pull a Docker image and you don't specify a tag it will use the default tag (usually &lt;code&gt;latest&lt;/code&gt;). Tags are the way you can keep track of changes in your project without overwriting previous versions. For example, if you (for some reason) are still using Python 2, you can access the Python 2 image by running &lt;code&gt;docker pull thejessleigh/goodreads-libib-converter:python2&lt;/code&gt;. Right now the &lt;code&gt;:python3&lt;/code&gt; and &lt;code&gt;latest&lt;/code&gt; tags on my rocker repo are the same, but you can pull either one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Pushing a docker image to Dockerhub
&lt;/h1&gt;

&lt;p&gt;Now that I have an image I want to put out into the world, I can push it up to Dockerhub.&lt;/p&gt;

&lt;p&gt;First, I need to log into Dockerhub and create a repository. Repositories require a name, and should have a short description which details the purpose of the project, and a long description that explains dependencies, requirements, build arguments, etc. You can also make a Docker repository private.&lt;/p&gt;

&lt;p&gt;Once I've done that, I run &lt;code&gt;docker push&lt;/code&gt;, which sends the latest commit of the project and tag I've specified up to the external registry. If you didn't specify a tag, this push will override the &lt;code&gt;latest&lt;/code&gt; tag in your repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker push thejessleigh/goodreads-libib-converter:python3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you go to &lt;a href="https://hub.docker.com/u/thejessleigh" rel="noopener noreferrer"&gt;my Dockerhub profile&lt;/a&gt; you can see the &lt;code&gt;goodreads-libib-converter&lt;/code&gt; project, and pull both the Python 2 and Python 3 incarnations.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next Steps
&lt;/h1&gt;

&lt;p&gt;Now that I have a working Docker image, I want to put it into production so that anyone can convert their Goodreads library CSV into a Libib library CSV. I'm going to go about this using AWS, which requires a bit of setup.&lt;/p&gt;

&lt;p&gt;The next installment in this series will go over setting up an AWS IAM account, setting up &lt;code&gt;awscli&lt;/code&gt; and configuring your local profiles, and creating an s3 bucket that your IAM account can access.&lt;/p&gt;

&lt;p&gt;EDIT: Never did get around to that next post in the series. I should do that someday.&lt;/p&gt;

</description>
      <category>python</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>explainlikeimfive</category>
    </item>
  </channel>
</rss>
