<?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>Forem: Christian Barra</title>
    <description>The latest articles on Forem by Christian Barra (@barrachri).</description>
    <link>https://forem.com/barrachri</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%2F97248%2F1b03871f-4c33-4db1-aba5-dde5c36f1c0d.jpg</url>
      <title>Forem: Christian Barra</title>
      <link>https://forem.com/barrachri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/barrachri"/>
    <language>en</language>
    <item>
      <title>How to run Python serverless applications on the Google Cloud Platform</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Wed, 13 Jan 2021 08:28:45 +0000</pubDate>
      <link>https://forem.com/barrachri/how-to-run-python-serverless-applications-on-the-google-cloud-platform-1jcm</link>
      <guid>https://forem.com/barrachri/how-to-run-python-serverless-applications-on-the-google-cloud-platform-1jcm</guid>
      <description>&lt;p&gt;I've used the Google Cloud Platform extensively in the last years and, as a cloud platform, is one of the most enjoyable you can work with.&lt;br&gt;
It really helps you focus on what matters, whether you are building an MVPs, a data ingestion pipeline, a single function or full-fledged style application.&lt;/p&gt;

&lt;p&gt;Let's focus on the serverless offer and discover how you can run Python serverless applications on top of GCP.&lt;/p&gt;

&lt;p&gt;There are 5 different ways in which you can deploy a Python application on GCP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A virtual machine (Compute Engine)&lt;/li&gt;
&lt;li&gt;A Kubernetes cluster (GKE)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Engine&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud Functions&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud Run&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;App Engine&lt;/strong&gt;, &lt;strong&gt;Cloud Functions&lt;/strong&gt; and &lt;strong&gt;Cloud Run&lt;/strong&gt; are 3 different ways to deploy serverless workloads on GCP.&lt;/p&gt;

&lt;p&gt;What does &lt;code&gt;serverless&lt;/code&gt; mean in this context?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don't need to pre-allocate resources (like number of machines)&lt;/li&gt;
&lt;li&gt;You pay (almost) zero if you don't receive any request&lt;/li&gt;
&lt;li&gt;It can scale up and down (to zero instances/functions) automagically&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Before starting: configure the gcloud sdk
&lt;/h3&gt;

&lt;p&gt;We are going to use the &lt;a href="https://cloud.google.com/sdk/docs/install"&gt;gcloud sdk&lt;/a&gt;, so it's better to have it setup properly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A valid &lt;a href="https://console.cloud.google.com/freetrial/"&gt;GCP account with a project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://cloud.google.com/sdk/docs/install"&gt;gcloud sdk&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;You need to configure the gcloud sdk, these are the magic lines you need to run:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# You can use whatever you like for CONFIGURATION-NAME&lt;/span&gt;
gcloud config configurations create &lt;span class="o"&gt;{&lt;/span&gt;CONFIGURATION-NAME&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# the project id comes from GCP&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project &lt;span class="o"&gt;{&lt;/span&gt;PROJECT-ID&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# This command will open a browser tab&lt;/span&gt;
gcloud auth login

&lt;span class="c"&gt;# Check if everything works&lt;/span&gt;
gcloud config configurations describe &lt;span class="o"&gt;{&lt;/span&gt;CONFIGURATION-NAME&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;You are ready!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  App Engine Standard Environment
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;App Engine has two versions, &lt;strong&gt;standard&lt;/strong&gt; and &lt;strong&gt;flexible environment&lt;/strong&gt;, here we are focusing on the standard version&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you are a fan of Heroku, App Engine is the closest experience you can get inside GCP.&lt;/p&gt;

&lt;p&gt;You need an &lt;code&gt;app.yaml&lt;/code&gt;, a &lt;code&gt;requirements.txt&lt;/code&gt; and then you are ready to go.&lt;/p&gt;

&lt;p&gt;There's a &lt;a href="https://github.com/py-bootcamp/movie-recommender/tree/app-engine"&gt;repository&lt;/a&gt; with a runnable example, with a few things to notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I use Poetry as dependency management tool and I export the requirements as &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;There's an &lt;code&gt;app.yaml&lt;/code&gt; and &lt;code&gt;.gcloudignore&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The app.yaml
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;app.yaml&lt;/code&gt; needs at least two entries: &lt;code&gt;runtime&lt;/code&gt; and &lt;code&gt;entrypoint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;App Engine supports both Python 3.7 and Python 3.8, so you can select the runtime you want to use.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;entrypoint&lt;/code&gt; is the command that you want to run (like &lt;code&gt;web&lt;/code&gt; in a Heroku procfile).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python38&lt;/span&gt;
&lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create and deploy an App Engine application
&lt;/h3&gt;

&lt;p&gt;After you cloned the repository there are only a few commands between having the application running locally and on App Engine&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;# You'll have to select a region and you can't change it afterwards&lt;/span&gt;
gcloud app create
gcloud app deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just wait a couple of minutes and your application should be running online. Pretty neat, isn't it?&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;I run some load tests using &lt;a href="https://github.com/rakyll/hey"&gt;hey&lt;/a&gt; with 5000 requests (&lt;code&gt;hey-n 5000 URL&lt;/code&gt;) and I got some really nice numbers. The application is not doing much, so I am basically testing the overhead of the GCP infrastructure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summary:
  Total:        5.4324 secs
  Slowest:      0.3870 secs
  Fastest:      0.0273 secs
  Average:      0.0517 secs
  Requests/sec: 920.3966
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet!&lt;/p&gt;

&lt;p&gt;It's also nice to see how the number of instances scales up and down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jGWN86mz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.pybootcamp.com/static/dc9826bbf62d63b27438a2d50bd75124/29007/scaling-up.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jGWN86mz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.pybootcamp.com/static/dc9826bbf62d63b27438a2d50bd75124/29007/scaling-up.png" alt="img"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--88A7aMDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.pybootcamp.com/static/d428b206d01f6db781cd3a93636551f2/29007/scaling-down.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--88A7aMDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.pybootcamp.com/static/d428b206d01f6db781cd3a93636551f2/29007/scaling-down.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Cloud Functions
&lt;/h3&gt;

&lt;p&gt;Cloud functions are the closest thing you can get inside the GCP ecosystem to AWS Lambda, with a key difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can trigger your cloud function directly, HTTP endpoints (triggers) are available out of the box&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Google recently announced the &lt;a href="https://cloud.google.com/api-gateway"&gt;GCP API Gateway&lt;/a&gt;&lt;/strong&gt;, now you can have the AWS Lambda &amp;amp; API Gateway experience also on GCP.&lt;/p&gt;

&lt;p&gt;You can trigger Cloud Functions with events (Cloud Storage, Pub/Sub, etc...) or by calling them directly.&lt;/p&gt;

&lt;p&gt;Let's deploy a cloud function, you can find a runnable example &lt;a href="https://github.com/py-bootcamp/movie-recommender/tree/cloud-functions"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is one main requirement: you need to have a &lt;code&gt;requirements.txt&lt;/code&gt; and a &lt;code&gt;main.py&lt;/code&gt; on your base path&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud functions deploy movie-recommender &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entry-point&lt;/span&gt; recommend_movie &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--runtime&lt;/span&gt; python38 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--trigger-http&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;europe-west1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--allow-unauthenticated&lt;/code&gt; makes your API endpoint public.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--entry-point&lt;/code&gt; specifies the function you want to call inside the &lt;code&gt;main.py&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloud Functions triggered by HTTP are based on Flask handlers, so our function receives a request object and needs to return something compatible with &lt;a href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response"&gt;make_reponse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Performance wise they are also good&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summary:
  Total:        10.5700 secs
  Slowest:      4.6717 secs
  Fastest:      0.0407 secs
  Average:      0.0779 secs
  Requests/sec: 473.0385
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cloud Run
&lt;/h3&gt;

&lt;p&gt;Cloud Run is a recent new addition to the serverless GCP suite, it uses knative to run serverless workloads on top of a Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;That means you can run Cloud Run on your GKE cluster or on a cluster managed directly by Google.&lt;/p&gt;

&lt;p&gt;Cloud Run offers a few pluses compared to App Engine and Cloud Functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You deploy containers&lt;/li&gt;
&lt;li&gt;One single instance can handle multiple requests at the same time&lt;/li&gt;
&lt;li&gt;Supports gRPC&lt;/li&gt;
&lt;li&gt;You are not limited by the supported languages or versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case you want to play with Cloud Run you can find (another) &lt;a href="https://github.com/py-bootcamp/movie-recommender/tree/cloud-run"&gt;runnable example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To deploy an application on top of Cloud Run you only need a docker image that can be pulled by the Cloud Run service.&lt;/p&gt;

&lt;p&gt;It can be a publicly available image, or an image hosted on the GCP Container Registry.&lt;/p&gt;

&lt;p&gt;In my case I decided to use the GCP Build service and push the image to the GCP Container registry.&lt;/p&gt;

&lt;p&gt;After you cloned the repository you can run this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud builds submit &lt;span class="nt"&gt;--tag&lt;/span&gt; eu.gcr.io/&lt;span class="nv"&gt;$YOUR_GCP_PROJECT&lt;/span&gt;/movie-recommender
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will build and push the docker image to the GCP Container Registry.&lt;/p&gt;

&lt;p&gt;Then we are ready to deploy the Cloud Run service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run deploy movie-recommender &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt; eu.gcr.io/&lt;span class="nv"&gt;$YOUR_GCP_PROJECT&lt;/span&gt;/movie-recommender &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; managed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; europe-west1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gunicorn,main:app,-w,4,-k,uvicorn.workers.UvicornWorker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explain each flag:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--allow-unauthenticated&lt;/code&gt;: same as cloud functions, enable a publicly reachable URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--platform&lt;/code&gt;: running on a Kubernetes cluster managed by Google&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--region&lt;/code&gt;: region in which I want to run my cloud run&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--args&lt;/code&gt;: this is the command I want to run inside the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And because we love benchmarks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summary:
  Total:        5.7388 secs
  Slowest:      0.2160 secs
  Fastest:      0.0386 secs
  Average:      0.0554 secs
  Requests/sec: 871.2571
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again really nice results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is serverless always the right answer?
&lt;/h3&gt;

&lt;p&gt;No, leaving cost aside there are certain things you can't do or can't do well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Websockets, GraphQL subscriptions and streaming are limited by the request timeout&lt;/li&gt;
&lt;li&gt;Headers and Payload size are also limited&lt;/li&gt;
&lt;li&gt;You can't create shiny network topologies and decide how requests are routed, you need to build on top of the GCP integrations (and limits)&lt;/li&gt;
&lt;li&gt;You need to be careful if you access services where the number of concurrent clients is limited, a good example is services connecting to PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;App Engine, Cloud Run and Cloud functions all come with a pretty good &lt;a href="https://cloud.google.com/free/docs/gcp-free-tier#free-tier"&gt;free tier&lt;/a&gt; and you pay only when you use them.&lt;/p&gt;

&lt;p&gt;They all support private VPC connections through &lt;a href="https://cloud.google.com/vpc/docs/configure-serverless-vpc-access"&gt;Serverless VPC access&lt;/a&gt;. Serverless VPC access is really neat, you can build complex topology on serverless stack, like mixing virtual machines and serverless applications inside the same network.&lt;/p&gt;

&lt;p&gt;The lock-in factor is also pretty low, especially for App Engine and Cloud Run.&lt;/p&gt;

&lt;p&gt;Being full-fledged applications you can pack them into containers and run them somewhere else.&lt;/p&gt;

&lt;p&gt;They are also well integrated with the Stackdriver products (now Operations), &lt;strong&gt;that means you get centralized log and monitoring out of the box&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And yes, &lt;strong&gt;you can use structlog and get you json logs parsed&lt;/strong&gt;, just remember to convert &lt;code&gt;level&lt;/code&gt; to &lt;code&gt;severity&lt;/code&gt; (&lt;a href="https://github.com/infarm/logicipi/blob/8aa6ff6d3250e2c763645e4fc26cb29b3cdd2a9b/src/logicipi/structlog.py#L20"&gt;check this snippet&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;App Engine and Cloud Run both &lt;strong&gt;support traffic splitting between different revisions&lt;/strong&gt;, so you get A/B testing or canary deployment for free.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If I had to choose one.... I'd go with Cloud Run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Cloud functions are also great but they force you writing web applications in a way that I don't like&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But I do love using Cloud Functions for event-based systems, data ingestion or pipeline based on events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pybootcamp.com/blog/how-to-write-dockerfile-python-apps/"&gt;How to Dockerize Python applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/api-gateway"&gt;API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine"&gt;App Engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions"&gt;Cloud Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/vpc/docs/configure-serverless-vpc-access"&gt;Serverless VPC access&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine/pricing"&gt;App Engine Pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/pricing"&gt;Cloud Functions Pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/pricing"&gt;Cloud Run Pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>serverless</category>
      <category>gcp</category>
    </item>
    <item>
      <title>Why you need enums and how to use them in Python</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Mon, 14 Dec 2020 14:12:18 +0000</pubDate>
      <link>https://forem.com/barrachri/why-you-need-enums-and-how-to-use-them-in-python-4nf2</link>
      <guid>https://forem.com/barrachri/why-you-need-enums-and-how-to-use-them-in-python-4nf2</guid>
      <description>&lt;p&gt;Let's say that you have a user object with two attributes: &lt;code&gt;name&lt;/code&gt; and  &lt;code&gt;type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A user's type can be &lt;code&gt;Admin&lt;/code&gt;, &lt;code&gt;Moderator&lt;/code&gt; and &lt;code&gt;Customer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How would you model it?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a Tuple
&lt;/h2&gt;

&lt;p&gt;One way is to use a tuple to hold type's values:&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="nn"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="n"&gt;USER_TYPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Moderator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Admin"&lt;/span&gt;&lt;span class="p"&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;dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try and see if a tuple can do the job:&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;# Admin
&lt;/span&gt;&lt;span class="n"&gt;user_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Christian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;USER_TYPES&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="c1"&gt;# Customer
&lt;/span&gt;&lt;span class="n"&gt;user_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Daniel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;USER_TYPES&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;user_a&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="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="c1"&gt;# Ideally it should be USER_TYPES 😞
&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;class 'str'&amp;gt;
&lt;/span&gt;
&lt;span class="c1"&gt;# Ideally this should be False 😞
&lt;/span&gt;&lt;span class="n"&gt;user_a&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="s"&gt;"Admin"&lt;/span&gt;
&lt;span class="c1"&gt;# True
&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USER_TYPES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 3
&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_type&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;USER_TYPES&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# ['Customer', 'Moderator', 'Admin']
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&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="s"&gt;"User is valid"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&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="s"&gt;"Not a valid user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# User is valid
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a good first attempt, but there are few caveats that I want to address:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;type(user_a.type)&lt;/code&gt; is &lt;code&gt;str&lt;/code&gt; and not &lt;code&gt;USER_TYPES&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_a.type == "Admin"&lt;/code&gt; is &lt;code&gt;True&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;who's gonna remember what &lt;code&gt;USER_TYPES[2]&lt;/code&gt; is?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Addressing the last point is easy, but the first two are tricky: &lt;strong&gt;I need to the separate the &lt;code&gt;UserType&lt;/code&gt; from its concrete representation (in this case a string)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Constants
&lt;/h2&gt;

&lt;p&gt;Another approach to consider is to assign numerical values to &lt;strong&gt;constants&lt;/strong&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&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;from&lt;/span&gt; &lt;span class="nn"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="n"&gt;CUSTOMER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MODERATOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADMIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;range&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="n"&gt;dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Christian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Daniel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CUSTOMER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;user_a&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="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;class 'int'&amp;gt;
&lt;/span&gt;
&lt;span class="n"&gt;user_a&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="s"&gt;"admin"&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="c1"&gt;# Ideally this should be False 😞
&lt;/span&gt;&lt;span class="n"&gt;user_b&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="mi"&gt;0&lt;/span&gt;
&lt;span class="c1"&gt;# True
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_b&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="n"&gt;CUSTOMER&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="s"&gt;"User type: CUSTOMER"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# User type: CUSTOMER
&lt;/span&gt;
&lt;span class="c1"&gt;# `user_b.type` should evaluate to True 😞
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&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="s"&gt;"User is valid"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&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="s"&gt;"Not a valid user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Not a valid user
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;constants&lt;/code&gt; really improves readability: &lt;strong&gt;I can now use &lt;code&gt;CUSTOMER&lt;/code&gt;, &lt;code&gt;MODERATOR&lt;/code&gt; and &lt;code&gt;ADMIN&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But it also has some (new) drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;user_b.type&lt;/code&gt; and &lt;code&gt;CUSTOMER&lt;/code&gt; are &lt;code&gt;0&lt;/code&gt;, so it's evaluated to &lt;a href="https://docs.python.org/3.8/library/stdtypes.html#truth-value-testing"&gt;&lt;code&gt;falsy&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type(user_a.type)&lt;/code&gt; is &lt;code&gt;int&lt;/code&gt; and not &lt;code&gt;UserType&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_b.type == 0&lt;/code&gt; is True&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;len(USER_TYPES)&lt;/code&gt; is gone, I don't have a way to reference to the collection of user's types&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using a Class
&lt;/h2&gt;

&lt;p&gt;Using a &lt;code&gt;class UserType&lt;/code&gt; can be a way to unify my first two attempts.&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="nn"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;CUSTOMER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;MODERATOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;ADMIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="c1"&gt;# NOTE: type is not UserType 😞
&lt;/span&gt;    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see how the &lt;code&gt;UserType&lt;/code&gt; behaves:&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;user_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&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="s"&gt;"Christian"&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="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&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="s"&gt;"Daniel"&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="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CUSTOMER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;user_a&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="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;class 'int'&amp;gt;
&lt;/span&gt;
&lt;span class="n"&gt;user_a&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="s"&gt;"ADMIN"&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="n"&gt;user_b&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="mi"&gt;0&lt;/span&gt;
&lt;span class="c1"&gt;# True
&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Traceback (most recent call last):
#   File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
# TypeError: object of type 'type' has no len()
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&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="s"&gt;"User is valid"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&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="s"&gt;"Not a valid user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Not a valid user 😞
&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_type&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Traceback (most recent call last):
#   File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
# TypeError: 'type' object is not iterable
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I grouped values together, under &lt;code&gt;UserType&lt;/code&gt;, and it's definitely more readable than before.&lt;/p&gt;

&lt;p&gt;But there are still a few drawbacks, similar to the &lt;code&gt;tuple&lt;/code&gt; implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the underlying concrete type for each value is &lt;code&gt;int&lt;/code&gt; so you can compare &lt;code&gt;UserType.CUSTOMER&lt;/code&gt; to other ints&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UserType.CUSTOMER&lt;/code&gt; is falsy, because its value is &lt;code&gt;0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;classes are mutable, and the fact that I can modify the class at runtime isn't exactly what I want&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Having a class that encapsulates the concept of type/category is useful, but a simple class is leaving us half way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I need a (new) type that supports enumeration, something that I can count, ideally immutable.&lt;/p&gt;

&lt;p&gt;And I need a (new) type where the underlying representation is somehow abstracted away, such as:&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="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type that I need is called &lt;code&gt;Enum&lt;/code&gt; and Python supports &lt;code&gt;Enum&lt;/code&gt;, they were introduced with &lt;a href="https://www.python.org/dev/peps/pep-0435/"&gt;PEP 435&lt;/a&gt; and they are part of the standard library.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From PEP 435: &lt;br&gt;&lt;br&gt;
An enumeration is a set of symbolic names bound to unique, constant values.&lt;br&gt;
Within an enumeration, the values can be compared by identity, and the enumeration itself can be iterated over.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Using Enums
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Enum types are data types that comprise a static, ordered set of values&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An example of an enum type might be the days of the week, or a set of status values for a piece of data (like my User's type).&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="nn"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;enum&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;CUSTOMER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;MODERATOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;ADMIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserType&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# exactly like with the class example
&lt;/span&gt;&lt;span class="n"&gt;user_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&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="s"&gt;"Christian"&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="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&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="s"&gt;"Daniel"&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="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CUSTOMER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;user_a&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="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;enum 'UserType'&amp;gt; 🚀
&lt;/span&gt;
&lt;span class="n"&gt;user_a&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="s"&gt;"ADMIN"&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="n"&gt;user_a&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="mi"&gt;2&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;
&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 3
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&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="s"&gt;"User is valid"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&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="s"&gt;"Not a valid user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# User is valid
&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_type&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# [&amp;lt;UserType.CUSTOMER: 0&amp;gt;, &amp;lt;UserType.MODERATOR: 1&amp;gt;, &amp;lt;UserType.ADMIN: 2&amp;gt;]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;So an &lt;code&gt;Enum&lt;/code&gt; is already more powerful than a simple class in modelling our business case&lt;/strong&gt;. It really helps us express some of the characteristics of a category/list.&lt;/p&gt;

&lt;p&gt;There are a couple of more things related to enums, some nice built-in features.&lt;/p&gt;

&lt;p&gt;I can access an &lt;code&gt;Enum&lt;/code&gt; in different ways, using both brackets and dot notations:&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;UserType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'ADMIN'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;UserType.ADMIN: 2&amp;gt;
&lt;/span&gt;
&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;UserType.ADMIN: 2&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each member of the &lt;code&gt;Enum&lt;/code&gt; has a &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; attribute:&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;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="c1"&gt;# 2
&lt;/span&gt;
&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="c1"&gt;# ADMIN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another neat feature is that you can't really modify an &lt;code&gt;Enum&lt;/code&gt; at runtime:&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="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# [&amp;lt;UserType.CUSTOMER: 0&amp;gt;, &amp;lt;UserType.MODERATOR: 1&amp;gt;, &amp;lt;UserType.ADMIN: 2&amp;gt;]
&lt;/span&gt;
&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NOOB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# [&amp;lt;UserType.CUSTOMER: 0&amp;gt;, &amp;lt;UserType.MODERATOR: 1&amp;gt;, &amp;lt;UserType.ADMIN: 2&amp;gt;]
&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MODERATOR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# True
&lt;/span&gt;
&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NOOB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# False
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see &lt;code&gt;enums&lt;/code&gt; in Python are really powerful, and they encapsulate well concepts like categories or types.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.python.org/3/library/enum.html"&gt;Python documentation&lt;/a&gt; is great and thoroughly covers all the details, I'd recommend checking it out.&lt;/p&gt;



&lt;h3&gt;
  
  
  Useful resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.python.org/dev/peps/pep-0435/"&gt;PEP 435&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/enum.html"&gt;Python Enum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/enum.html#how-are-enums-different"&gt;How are Enums different?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Enumerated_type"&gt;Enumerated type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sqlalchemy.org/en/13/core/type_basics.html#sqlalchemy.types.Enum"&gt;SQL Alchemy Enum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.djangoproject.com/en/3.1/ref/models/fields/#enumeration-types"&gt;Django Enumeration Types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://www.python.org/dev/peps/pep-0008/#constants"&gt;Python constants&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>How to write a great Dockerfile for Python apps</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Mon, 26 Oct 2020 12:11:47 +0000</pubDate>
      <link>https://forem.com/barrachri/how-to-write-a-great-dockerfile-for-python-apps-56g6</link>
      <guid>https://forem.com/barrachri/how-to-write-a-great-dockerfile-for-python-apps-56g6</guid>
      <description>&lt;p&gt;In the previous article, &lt;a href="/blog/how-to-containerize-python-application/" title="How to containerize a Python Application"&gt;how to containerize a Python application&lt;/a&gt;, we created a Dockerfile and containerized our application from scratch.&lt;/p&gt;

&lt;p&gt;Now we really want our Dockerfile to stand out, &lt;strong&gt;make it more production-ready&lt;/strong&gt;, that's the goal of this article.&lt;/p&gt;

&lt;p&gt;We will cover 6 different ways to improve our &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setting env variables and a working directory&lt;/li&gt;
&lt;li&gt;avoiding invalidating the Docker cache&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;changing the default user to non-root&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;taking care of zombie processes&lt;/li&gt;
&lt;li&gt;correctly forwarding signals to our application&lt;/li&gt;
&lt;li&gt;updating &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;setuptools&lt;/code&gt; and &lt;code&gt;wheel&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;This is the &lt;code&gt;Dockerfile&lt;/code&gt; we created last time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.8.3-slim-buster&lt;/span&gt;

&lt;span class="c"&gt;# 2. Copy files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /src&lt;/span&gt;

&lt;span class="c"&gt;# 3. Install our deps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /src/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While fully functional, there are a few things we can improve regarding usability, security and performance.&lt;/p&gt;

&lt;p&gt;You can clone this &lt;a href="https://github.com/py-bootcamp/movie-recommender/tree/part-1"&gt;repository&lt;/a&gt; if you want to follow along.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passing the git commit hash
&lt;/h3&gt;

&lt;p&gt;We want to &lt;em&gt;mark&lt;/em&gt; each Docker image and container with a tag, this tag is the &lt;code&gt;git commit hash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At runtime we should be able to determine which version of our software we are running.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The idea is that every artifact we generate is traceable, we can go back and check which commit generated it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/engine/reference/builder/#arg"&gt;ARG&lt;/a&gt; and &lt;a href="https://docs.docker.com/engine/reference/builder/#env"&gt;ENV&lt;/a&gt; instructions can help us achieving it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ARG&lt;/code&gt; specifies arguments that we can pass to the &lt;code&gt;docker build&lt;/code&gt; command, &lt;code&gt;ENV&lt;/code&gt; are env variables set inside the &lt;code&gt;Dockerfile&lt;/code&gt; and accessible at runtime, from within the container.&lt;/p&gt;

&lt;p&gt;This is the new &lt;code&gt;Dockerfile&lt;/code&gt;, with &lt;code&gt;ARG&lt;/code&gt; and &lt;code&gt;ENV&lt;/code&gt;:&lt;br&gt;
&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.8.3-slim-buster&lt;/span&gt;

&lt;span class="c"&gt;# 👇&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH=${GIT_HASH:-dev}&lt;/span&gt;
&lt;span class="c"&gt;# 👆&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /src&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /src/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-dev&lt;/code&gt; is a way to specify defaults. If the &lt;code&gt;GIT_HASH&lt;/code&gt; argument is omitted then &lt;code&gt;GIT_HASH&lt;/code&gt; will be set to &lt;code&gt;dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's build our Docker image and check the &lt;code&gt;GIT_HASH&lt;/code&gt; env variable:&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="o"&gt;&amp;gt;&lt;/span&gt; docker build &lt;span class="nt"&gt;-t&lt;/span&gt; movie-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; movie-app &lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;GIT_HASH
&lt;span class="nv"&gt;GIT_HASH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How do we pass the git commit hash to our Docker image?&lt;/p&gt;

&lt;p&gt;We use the &lt;a href="https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg"&gt;&lt;code&gt;--build-arg&lt;/code&gt;&lt;/a&gt; flag from the Docker cli:&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;# this will work on bash but not on 🐟&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GIT_HASH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker build &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;GIT_HASH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_HASH&lt;/span&gt;::7&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; movie-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; movie-app &lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;GIT_HASH
&lt;span class="nv"&gt;GIT_HASH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6a78e6b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't need the whole commit hash, the first 7 characters are enough.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why are we not passing the base image using &lt;code&gt;ARG&lt;/code&gt;?&lt;br&gt;
Because we don't want to change the base Docker image from the Docker cli, but only through a new commit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding a working directory
&lt;/h3&gt;

&lt;p&gt;Right now we are copying our files inside a &lt;code&gt;/src&lt;/code&gt; folder and then we specify all the other paths relative to &lt;code&gt;/src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Wouldn't be nicer if we could specify a working directory and run commands from that folder?&lt;/p&gt;

&lt;p&gt;That would be neat, and &lt;a href="https://docs.Docker.com/engine/reference/builder/#workdir"&gt;WORKDIR&lt;/a&gt; is exactly what we need.&lt;br&gt;
&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.8.3-slim-buster&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH=${GIT_HASH:-dev}&lt;/span&gt;

&lt;span class="c"&gt;# 👇&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /project&lt;/span&gt;

&lt;span class="c"&gt;# 👇&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# 👇&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we specify a &lt;code&gt;WORKDIR&lt;/code&gt;, any &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;, &lt;code&gt;ENTRYPOINT&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;ADD&lt;/code&gt; instructions that follow will use that working directory.&lt;/p&gt;

&lt;p&gt;Note how the path of &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;pip install&lt;/code&gt; changed.&lt;/p&gt;

&lt;p&gt;Let's test our application:&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 build -t movie-app .
# 💥 it's not python /src/app.py anymore 💥
&amp;gt; docker run --rm -p 8888:8888 movie-app python app.py
&amp;gt; curl localhost:8888
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Caching dependencies
&lt;/h3&gt;

&lt;p&gt;Our application has a small number of external dependencies, the &lt;code&gt;requirements.txt&lt;/code&gt; contains only a few dependencies, so the &lt;code&gt;pip install&lt;/code&gt; command is fast, just a couple of seconds.&lt;/p&gt;

&lt;p&gt;What if it were taking minutes instead of seconds?&lt;/p&gt;

&lt;p&gt;Wouldn't be better to cache our dependencies until something changes?&lt;/p&gt;

&lt;p&gt;If you try to modify any file inside our application's folder and try to run the Docker build command you will see how Docker builds the image starting from zero.&lt;/p&gt;

&lt;p&gt;If you check the console output you should see something 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;Step 6/7 : RUN pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;2233484e3f72
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically any change to our codebase, even if it's not related to &lt;code&gt;requirements.txt&lt;/code&gt; will invalidate the Docker cache.&lt;/p&gt;

&lt;p&gt;We can be smarter and save some time, &lt;strong&gt;we just need to install our dependencies first&lt;/strong&gt;.&lt;br&gt;
&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.8.3-slim-buster&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH=${GIT_HASH:-dev}&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /project&lt;/span&gt;

&lt;span class="c"&gt;# 👇&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="c"&gt;# 👆&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added a new &lt;code&gt;COPY&lt;/code&gt;, just for &lt;code&gt;requirements.txt&lt;/code&gt;, and moved the &lt;code&gt;pip install&lt;/code&gt; right after.&lt;/p&gt;

&lt;p&gt;If you now try to build the Docker image again, then change the &lt;code&gt;main.py&lt;/code&gt; and rerun the &lt;code&gt;docker build&lt;/code&gt; command again that shouldn't invalidate the cache.&lt;/p&gt;

&lt;p&gt;This is the output you should see, &lt;code&gt;Using cache&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; docker build -t movie-app .
Step 6/7 : RUN pip install -r requirements.txt
 ---&amp;gt; Using cache
 ---&amp;gt; cbe7b2865e10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running your container as non-root user
&lt;/h3&gt;

&lt;p&gt;By default the user running your command inside a Docker container is &lt;code&gt;root&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; docker run --rm movie-app whoami
root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Long story short, Docker containers should not run as root and is highly recommended to change the default user to a non-root user.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How do we change the user?&lt;/p&gt;

&lt;p&gt;We create a new one and we set the new user with the &lt;a href="https://docs.Docker.com/engine/reference/builder/#user"&gt;USER&lt;/a&gt; instruction.&lt;br&gt;
&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.8.3-slim-buster&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH=${GIT_HASH:-dev}&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /project&lt;/span&gt;

&lt;span class="c"&gt;# here we create a new user&lt;/span&gt;
&lt;span class="c"&gt;# note how the commands are using &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="c"&gt;# this helps with caching&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; user &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown &lt;/span&gt;user /project
&lt;span class="c"&gt;# 👆&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# 👇 here we set the user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; docker build -t movie-app .
&amp;gt; docker run --rm movie-app whoami
user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;user&lt;/code&gt; can't create new files outside of the &lt;code&gt;/project&lt;/code&gt; folder (user is the owner of the folder):&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 run --rm touch /hello
touch: cannot touch '/hello': Permission denied
# 👇 but this command would work
&amp;gt; docker run --rm touch hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's test our application to be sure it has all the necessary permissions:&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="o"&gt;&amp;gt;&lt;/span&gt; docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8888:8888 movie-app python app.py
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl localhost:8888
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Taking care of zombie processes and signals
&lt;/h3&gt;

&lt;p&gt;Each Docker container is a PID namespace, and A PID namespace is a tree, which starts at PID 1, commonly called init.&lt;/p&gt;

&lt;p&gt;The entire process of starting the system and shutting it down is maintained by init, when you run a Docker container, PID 1 is what you set inside your &lt;code&gt;ENTRYPOINT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you don't set it by default Docker will use &lt;code&gt;/bin/sh -c&lt;/code&gt;, which does not pass signals, making almost impossible to gracefully stop your application.&lt;/p&gt;

&lt;p&gt;This is why we need a better init, &lt;a href="https://github.com/krallin/tini"&gt;Tini&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Tini&lt;/code&gt; doesn't only take care of reaping zombie processes but also of forwarding any signals we send to the Docker container to our application process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Forwarding signals correctly is really important. Kubernetes relies on signals during the lifecycle of a pod.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination"&gt;More about Kubernetes and signals here&lt;/a&gt;.&lt;br&gt;
&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.8.3-slim-buster&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH=${GIT_HASH:-dev}&lt;/span&gt;
&lt;span class="c"&gt;# 👇 you can use env variables to pin library versions&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TINI_VERSION="v0.19.0"&lt;/span&gt;

&lt;span class="c"&gt;# 👇&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /tini
&lt;span class="c"&gt;# 👆&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /project&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; user &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown &lt;/span&gt;user /project

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; user&lt;/span&gt;

&lt;span class="c"&gt;# 👇&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/tini", "--"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have two new instructions here, &lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint"&gt;ADD&lt;/a&gt; and &lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint"&gt;ENTRYPOINT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ADD&lt;/code&gt; is a really useful instruction, it can add remote files to you Docker image.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ENTRYPOINT&lt;/code&gt; specifies the entry point for any command, in our case &lt;code&gt;python app.py&lt;/code&gt;, pretty much like running &lt;code&gt;/tini -- python app.py&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating pip, setuptools and wheel
&lt;/h3&gt;

&lt;p&gt;One last thing, it's important to keep &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;setuptools&lt;/code&gt; and &lt;code&gt;wheel&lt;/code&gt; updated, so it's wise to bump them directly inside our Docker image.&lt;br&gt;
&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.8.3-slim-buster&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GIT_HASH=${GIT_HASH:-dev}&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TINI_VERSION="v0.19.0"&lt;/span&gt;

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /tini

&lt;span class="c"&gt;# 👇 STYLE YOUR DOCKERFILE LIKE A PRO&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    pip &lt;span class="se"&gt;\
&lt;/span&gt;    setuptools &lt;span class="se"&gt;\
&lt;/span&gt;    wheel

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /project&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; user &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown &lt;/span&gt;user /project

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; user&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/tini", "--"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's test our application once again:&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 build -t movie-app .
&amp;gt; docker run --rm -p 8888:8888 movie-app python app.py
curl localhost:8888
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with this last step we are done!&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick recap
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ARG&lt;/code&gt; and &lt;code&gt;ENV&lt;/code&gt; are neat, use them&lt;/li&gt;
&lt;li&gt;Copy and install your dependencies before copying your application&lt;/li&gt;
&lt;li&gt;Don't run containers as root, set a new user with &lt;code&gt;USER&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Try to prettify your dockerfiles&lt;/li&gt;
&lt;li&gt;Always use &lt;code&gt;Tini&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Defining a &lt;code&gt;WORKDIR&lt;/code&gt; helps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Useful resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/py-bootcamp/movie-recommender/tree/part-2"&gt;Github repo for movie-recommender&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/#arg"&gt;ARG&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/#env"&gt;ENV&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.Docker.com/engine/reference/builder/#workdir"&gt;WORKDIR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.Docker.com/engine/reference/builder/#user"&gt;USER&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint"&gt;ENTRYPOINT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>containers</category>
      <category>docker</category>
      <category>dockerfile</category>
      <category>python</category>
    </item>
    <item>
      <title>How to containerize a Python Application</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Mon, 17 Aug 2020 17:11:00 +0000</pubDate>
      <link>https://forem.com/barrachri/how-to-containerize-a-python-application-415d</link>
      <guid>https://forem.com/barrachri/how-to-containerize-a-python-application-415d</guid>
      <description>&lt;h5&gt;
  
  
  Containers are everywhere, but how do you run a Python application inside a Docker container? This article will show you how!
&lt;/h5&gt;

&lt;p&gt;&lt;em&gt;In case you are wondering, the examples require Python 3.x&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Before diving into containers, let’s talk a bit more about the Python application we want to containerize.&lt;/p&gt;

&lt;p&gt;The application is a web API that returns a random movie from a movie collection. Inside our local folder we have 3 files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;app.py &lt;span class="c"&gt;# Python application&lt;/span&gt;
movies.json &lt;span class="c"&gt;# movie collection&lt;/span&gt;
requirements.txt &lt;span class="c"&gt;# where we specifiy our Python dependencies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt; contains one API endpoint that returns a random movie:&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="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cherrypy&lt;/span&gt;

&lt;span class="n"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'PORT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8888&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;FOLDER_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&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="n"&gt;parent&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FOLDER_PATH&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;"movies.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&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;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;MOVIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cherrypy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expose&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cherrypy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json_out&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;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"movie"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MOVIES&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="n"&gt;cherrypy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quickstart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cherrypy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="s"&gt;'server.socket_host'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'0.0.0.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'server.socket_port'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PORT&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;Inside &lt;code&gt;requirements.txt&lt;/code&gt;, we have our dependencies and after running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we are able to run our application with &lt;code&gt;python app.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;curl localhost:8888&lt;/code&gt; should return a random movie, something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"movie"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Opal Dreams"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"US_Gross"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Worldwide_Gross"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"US_DVD_Sales"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Production_Budget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Release_Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nov 22 2006"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MPAA_Rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Running_Time_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Distributor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Strand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Based on Book/Short Story"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Major_Genre"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Drama"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Creative_Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Contemporary Fiction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Director"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Rotten_Tomatoes_Rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"IMDB_Rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"IMDB_Votes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;468&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool, the application is working!&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we containerize our application?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Containerizing a Python application means creating a Docker image that has everything needed to run it: source code, dependencies and configuration.&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The first step to containerize our application is to create a new text file, named &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;app.py
movies.json
requirements.txt
Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;Dockerfile&lt;/code&gt; (for what we consider a minimal viable &lt;code&gt;Dockerfile&lt;/code&gt;), we need to specify three steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the base image we want to use&lt;/li&gt;
&lt;li&gt;Select the files we want to copy inside the Docker image&lt;/li&gt;
&lt;li&gt;Install the app's dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Base image
&lt;/h3&gt;

&lt;p&gt;To specify a base image, we use the &lt;a href="https://docs.Docker.com/engine/reference/builder/#from"&gt;FROM command&lt;/a&gt; followed by either a private or public image.&lt;/p&gt;

&lt;p&gt;In our case, we will use the &lt;a href="https://hub.Docker.com/_/python"&gt;official Python Docker image&lt;/a&gt;, available on the Docker hub.&lt;/p&gt;

&lt;p&gt;We will select the latest available Python 3 image based on Ubuntu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.8.5-slim-buster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image name is made up of two different parts: &lt;code&gt;image:tag&lt;/code&gt;. In our case the image is &lt;code&gt;python&lt;/code&gt; and the tag is &lt;code&gt;3.8.5-slim-buster&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's everything we need for the first step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy our application
&lt;/h3&gt;

&lt;p&gt;To copy our application inside the Docker image, we will use the &lt;a href="https://docs.Docker.com/engine/reference/builder/#copy"&gt;COPY command&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 2. Copy files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /src&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command copies the specified files (or folder) into the Docker image. In our case, we want to copy all files available in our local folder inside the Docker image under the &lt;code&gt;/src&lt;/code&gt; path.&lt;/p&gt;

&lt;p&gt;It's important to note that the first part of the &lt;code&gt;COPY&lt;/code&gt; command is a path relative to the context of the build and not to our local machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the requirements.txt
&lt;/h3&gt;

&lt;p&gt;The last step is to install our dependencies inside the Docker image. To achieve that we will use the &lt;a href="https://docs.Docker.com/engine/reference/builder/#run"&gt;RUN command&lt;/a&gt; to &lt;code&gt;RUN&lt;/code&gt; pip install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 3. Install our deps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /src/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to notice is that the path of &lt;code&gt;requirements.txt&lt;/code&gt; is different compared with the first time we ran &lt;code&gt;pip install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason behind this is that the copied files are under the &lt;code&gt;/src&lt;/code&gt; path inside the image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build and run a Docker image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.8.3-slim-buster&lt;/span&gt;

&lt;span class="c"&gt;# 2. Copy files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /src&lt;/span&gt;

&lt;span class="c"&gt;# 3. Install our deps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /src/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;Dockerfile&lt;/code&gt; is now complete and we can use it to build a Docker image. For that we need to use the &lt;code&gt;docker build&lt;/code&gt; command:&lt;br&gt;
&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="nt"&gt;-t&lt;/span&gt; movie-recommender &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command builds a Docker image named &lt;code&gt;movie-recommender&lt;/code&gt; using your current folder as building context (the &lt;code&gt;.&lt;/code&gt; at the end specifies the path of the building context we want to use).&lt;/p&gt;

&lt;p&gt;Now we can run the image we just built using the &lt;code&gt;docker run&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run movie-recommender python /src/app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will execute &lt;code&gt;python /src/app.py&lt;/code&gt; inside a container based on the &lt;code&gt;movie-recommender&lt;/code&gt; image.&lt;/p&gt;

&lt;p&gt;However, if we try to connect to our application using &lt;code&gt;curl localhost:8888&lt;/code&gt;, we will get an error.&lt;/p&gt;

&lt;p&gt;How is that possible? Why can't we connect to our application that runs inside the container?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reason is that we didn’t expose our application's port to the local machine&lt;/strong&gt;. We can do that by using the &lt;a href="https://docs.docker.com/engine/reference/run/#expose-incoming-ports"&gt;&lt;code&gt;-p HostPort:ContainerPort&lt;/code&gt; flag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let’s try to rerun the command again, this time specifying that we want to expose port &lt;code&gt;8888&lt;/code&gt; locally:&lt;br&gt;
&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;-p&lt;/span&gt; 8888:8888 movie-recommender python /src/app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then &lt;code&gt;curl localhost:8888&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now it works—great! We just containerized a Python application!&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Containerizing an application means to create a Docker image that has everything needed to run it—source code, libraries, and configuration.&lt;/li&gt;
&lt;li&gt;We used a Dockerfile to specify how to build a Docker image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker build&lt;/code&gt; builds a Docker image starting from a &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker run&lt;/code&gt; runs a container using a Docker image.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Useful resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/py-bootcamp/movie-recommender/tree/part-1"&gt;Github repo for movie-recommender&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.Docker.com/engine/reference/builder/"&gt;Dockerfile reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.Docker.com/engine/reference/run/"&gt;Docker run&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.Docker.com/engine/reference/commandline/build/"&gt;Docker build&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>python</category>
    </item>
    <item>
      <title>Freelancing &amp; Academia to Industry panel</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Fri, 25 Oct 2019 17:02:10 +0000</pubDate>
      <link>https://forem.com/barrachri/freelancing-academia-to-industry-panel-3f0i</link>
      <guid>https://forem.com/barrachri/freelancing-academia-to-industry-panel-3f0i</guid>
      <description>&lt;p&gt;On October 11th we hosted a &lt;a href="https://de.pycon.org/program/pyconde-meegjc-job-panel-christian-barra-tereza-iofciu-katharina-rasch-matteo-guzzo-sieer-angar/"&gt;panel at PyConDE&lt;/a&gt; with &lt;a href="https://twitter.com/_teoguso"&gt;Matteo Guzzo&lt;/a&gt;, &lt;a href="https://twitter.com/terezaif"&gt;Tereza Iofciu&lt;/a&gt;, &lt;a href="https://twitter.com/krasch_io"&gt;Katharina Rasch&lt;/a&gt; and &lt;a href="https://twitter.com/sieer83/"&gt;Sieer Angar&lt;/a&gt; and I had the pleasure to be the moderator.&lt;/p&gt;

&lt;p&gt;These are some of the takeaways:&lt;/p&gt;

&lt;h3&gt;
  
  
  From Academia to Industry
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;1) &lt;strong&gt;As a PhD student you learn how to learn&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will be extremely useful to help you moving to industry and to learn new skills as fast as possible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;2) &lt;strong&gt;Do your homework, be prepared&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you don't want to be perceived as a junior during your interviews prepare yourself beforehand, try to understand what companies are looking for and how to approach the interview correctly.&lt;br&gt;
If the interview process is something new for you have some dry runs with companies you don't want to work for.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;3) &lt;strong&gt;There are pros and cons on both worlds&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://twitter.com/krasch_io"&gt;Katharina Rasch&lt;/a&gt; keeps teaching although working as a freelancer and consultant.&lt;br&gt;
You can move to industry without fully leaving academia.&lt;/p&gt;

&lt;h3&gt;
  
  
  Freelancing
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;1) &lt;strong&gt;Wanna be a freelancer? Just do it&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you think you want to do it just do it, you always have a rollback plan: finding another job as employee.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;2) &lt;strong&gt;You have to create and grow your network&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meetups, conferences and generally sharing your knowledge is a great way to do it, make yourself visible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;3) &lt;strong&gt;Freelancing platforms can be a great way to start&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.pivigo.com/"&gt;Pivigo&lt;/a&gt;, &lt;a href="https://www.fiverr.com"&gt;Fiverr&lt;/a&gt;, &lt;a href="https://www.toptal.com"&gt;Toptal&lt;/a&gt;, &lt;a href="https://www.upwork.com/"&gt;Upwork&lt;/a&gt; are a great way to kick-start your way to freelancing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;4) &lt;strong&gt;Create a cash buffer&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having a cash buffer is fundamental if you want to be able to sustain your first months as a freelancer, that will give you financial freedom and reduce stress.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;5) &lt;strong&gt;You rates will suck at the beginning&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's fine. But don't forget to raise them and keep them updated considerinng market rates.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;6) &lt;strong&gt;Berlin is a bubble&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While knowing German might not be a requirement in Berlin the rest of Germany is different although things are changing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;7) &lt;strong&gt;Just freely approach companies&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to collaborate with them and/or create new connections as a freelancer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;8) &lt;strong&gt;Take care of the bureaucratic stuff&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Being a freelancer involves some bureaucratic burdens you have to take care like accounting and health insurance.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The panel has been recorded, it will be available on the &lt;a href="https://www.youtube.com/channel/UCji5VWDkGzuRenyRQZ9OpFQ"&gt;PyCon DE youtube channel&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>freelancing</category>
      <category>academia</category>
      <category>industry</category>
    </item>
    <item>
      <title>How to deal with self-limiting thought before a talk</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Mon, 07 Oct 2019 12:48:03 +0000</pubDate>
      <link>https://forem.com/barrachri/how-to-deal-with-self-limiting-thought-before-a-talk-2c1h</link>
      <guid>https://forem.com/barrachri/how-to-deal-with-self-limiting-thought-before-a-talk-2c1h</guid>
      <description>&lt;p&gt;A couple of days ago a friend wrote me a message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you check my presentation?&lt;br&gt;
I gave it today and I think it went really bad.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I checked his slides and they were good, same for his notes, so the problem was somewhere else.&lt;/p&gt;

&lt;p&gt;So I asked: &lt;strong&gt;How did you feel during your talk?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was really tense&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, that can be somehow normal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What were you thinking during your talk?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am doing it wrong&lt;/p&gt;

&lt;p&gt;This slide is wrong&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ouch, self-limiting thought.&lt;/p&gt;

&lt;h4&gt;
  
  
  Climbing and public speaking
&lt;/h4&gt;

&lt;p&gt;I find many similarities between climbing and public speaking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The only difference between a climber who climbs 5.15 and one who climbs 5.5 is the magnitude of the doubt and the skills developed to deal with it.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arno Ilgner&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is probably what separates giving a talk you want to forget about from giving a great talk.&lt;/p&gt;

&lt;p&gt;Doubts and self-limiting thought are everywhere and as soon as you face discomfort (jumping on the stage) they are gonna reach your mind at light speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So how do you deal with it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you deal with self-limiting thought and with your mind hectically wandering around?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well I usually do 3 things.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Take some time before the talk just for you
&lt;/h3&gt;

&lt;p&gt;I always try to have 5 mins just for me before a talk, you don't have to meditate in a zazen posture, I sit somewhere comfortably and just relax.&lt;/p&gt;

&lt;p&gt;I want to enter an inner state of &lt;code&gt;relaxed concentration&lt;/code&gt;, where I am focused on &lt;code&gt;the message you want to deliver&lt;/code&gt; and relaxed, with a positive thinking mindset (I am gonna do great).&lt;/p&gt;

&lt;p&gt;The last thing that I do is power posing.&lt;/p&gt;

&lt;p&gt;Yep,  I go to something that I consider a private space (a toilette works well) and I do 30 seconds of power posing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Breathe, consciously and continuously. Shifting your focus from the intruding thoughts to your breathing makes a bridge back to the body, to the flow of action and movement. Keep moving.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arno Ilgner&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Focus and remember to breathe
&lt;/h3&gt;

&lt;p&gt;I always try to keep the attention in the moment and remember to breathe.&lt;/p&gt;

&lt;p&gt;It's so easy to hold your breath and forget about breathing but it's rather... crucial.&lt;/p&gt;

&lt;p&gt;The inhale/exhal process is a great way to focus your attention on your body and control your stress level during your talk.&lt;/p&gt;

&lt;p&gt;And as you breathe you take pauses which help giving the right rhythm to your talk.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don’t react to the doubts. Rather, stay in the pose with the stress and delay reacting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arno Ilgner&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Delay
&lt;/h3&gt;

&lt;p&gt;If a self-limit thought comes to my mind I try to don't react, don't fight with it, just ackownledge the thought and let it go.&lt;/p&gt;

&lt;p&gt;The internal dialogue should go like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I am aware my mind thinks this slide sucks... but I am gonna deal with this thought later."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  And you? how do you deal with self-limiting thought before a talk?
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Useful resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/7292000-espresso-lessons"&gt;Arno Ilgner - Espresso Lessons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/501200.The_Rock_Warrior_s_Way"&gt;Arno Ilgner - The Rock Warrior's Way&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ted.com/talks/amy_cuddy_your_body_language_shapes_who_you_are?language=en"&gt;Amy Cudd - Power Posing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=a5WIMg5sunw"&gt;Yenny Cheung - Why You Should Pursue Public Speaking and How to Get There&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;5.5 and 5.15 are part of the climbing grading system, &lt;a href="https://www.guidedolomiti.com/en/rock-climbing-grades/"&gt;5.5 is beginner, 5.15 is super elite&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>publicspeaking</category>
      <category>selflimitingthought</category>
      <category>mindfulness</category>
      <category>stress</category>
    </item>
    <item>
      <title>Onboarding for new tech hires</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Wed, 13 Feb 2019 11:51:57 +0000</pubDate>
      <link>https://forem.com/barrachri/onboarding-for-new-tech-hires-3n6b</link>
      <guid>https://forem.com/barrachri/onboarding-for-new-tech-hires-3n6b</guid>
      <description>&lt;p&gt;Hiring a new member for a tech team is such a long process that as soon as the contract is signed you would like to think that your job is done.&lt;/p&gt;

&lt;p&gt;But is it?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No exactly, your hiring process is not yet done, the hardest part has still to be completed, &lt;strong&gt;the onboarding&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What is onboarding?
&lt;/h3&gt;

&lt;p&gt;Wikipedia defines onboarding as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the mechanism through which new employees acquire the necessary knowledge, skills, and behaviors in order to become effective organizational members and insiders&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So &lt;strong&gt;knowledge&lt;/strong&gt;, &lt;strong&gt;skills&lt;/strong&gt; and &lt;strong&gt;behaviors&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do you need it?
&lt;/h3&gt;

&lt;p&gt;You spent around a month, during a long (and maybe painful) process, aren't we done? why spend more time?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You want the new team member to be productive as soon as possible and making them happy will probably increase the productiveness and chances that they will stay for a longer time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;First impression matters, this is why onboarding is fundamental.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Giving your new employees an amazing experience is generally better than providing an average or bad experience, so why wondering at all?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;And above all it will show that they are important&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So how do you provide a great onboarding experience? &lt;strong&gt;You need a plan and a goal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The onboarding process needs to have a goal, something that is easy to measure or consider done (creating a PR, merging some code, releasing something to production....).&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to create a great plan
&lt;/h3&gt;

&lt;p&gt;You need someone in charge of the process, a tech lead, senior engineer for example, someone that could guide the new hire but is not the line manager, we will call it buddy.&lt;/p&gt;

&lt;p&gt;The buddy doesn't have to spend 24 hours with them but should be generally available and responsible for the entire process and should be her/his main point of contact for any question related to the onboarding process and its goals.&lt;/p&gt;

&lt;p&gt;So the PIPitRight Team just hired a new awesome member Matteo, and the buddy for him is a Senior Backend Developer member of the same team called Luzia.&lt;/p&gt;

&lt;p&gt;The goal is to fix an unclosed socket error, a bug that is afflicting a service and to release the fix to prod.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ideal plan
&lt;/h3&gt;

&lt;p&gt;The ideal plan is divided into 4 phases (at least):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Before the first day&lt;/li&gt;
&lt;li&gt;During the first day&lt;/li&gt;
&lt;li&gt;During the first weeks&lt;/li&gt;
&lt;li&gt;Follow up&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article will cover the &lt;code&gt;Before the first day&lt;/code&gt; and in the next weeks I will cover the remaining phases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before the first day
&lt;/h3&gt;

&lt;p&gt;There are a couple of things you need to prepare before she/he joins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Internally share that a new hire will join the team&lt;/li&gt;
&lt;li&gt;Prepp all the accounts/services he/she needs&lt;/li&gt;
&lt;li&gt;Share the onboarding plan with the new hire&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  1. Share with the rest of the team who is joining and a brief bio
&lt;/h4&gt;

&lt;p&gt;This sounds super easy but is easily forgotten or overlooked, especially if you are a remote team.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There's nothing worse than having new people joining your team and you have no clue who the new name inside slack channels is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I personally find really important (and often disturbing when it doesn't happen) that a new hire is not introduced properly.&lt;/p&gt;

&lt;p&gt;The buddy (or a team lead/engineering manager) should introduce the new hire with an email or a message on slack for example.&lt;/p&gt;

&lt;p&gt;If your team as regular meetings announcing it there is also fine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello PIPitRight Team!

I am really happy to announce that in the next weeks Matteo will join our team as a Backend Developer.

He is been a ..... (short bio).

He will join the team on .../.../...

Looking forward to working together!

Best,
Luzia
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It takes less than 5 minutes, just do it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Accounts are ready
&lt;/h4&gt;

&lt;p&gt;Provide him/her with the accounts he/she needs email, github, slack, aws, gcp, heroku and all the related services, a nice wiki about how to obtain them is awesome, especially if there are external teams involved (security, ops...).&lt;/p&gt;

&lt;p&gt;In many companies is just a matter of creating a new ticket/task.&lt;/p&gt;

&lt;h4&gt;
  
  
  Having milestones and a task list for each day
&lt;/h4&gt;

&lt;p&gt;Sharing the onboarding plan beforehand with the hire is extremely useful and doesn't require too much time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hi Matteo,

I am really happy you will the PIPitRight team!

I am Luzia and I will be your buddy for the first 2 weeks.

We will pair together a couple of times during these weeks and I will introduce you through our tasks system and we will move a couple of tickets together from the backlog to done.

The goal for the 2 weeks is to fix an unclosed socket error, a bug that is afflicting a production service and to release the fix to prod. (ISSUE #4343).

It's going to be a lot of fun, I can't wait for that!

Best,
Luzia
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;p&gt;My approach is to avoid overwhelming a new hire and properly manage the cognitive load, with a small and focused scope that would increase during time.&lt;/p&gt;

&lt;p&gt;For example having a 360 degrees and deep overview of the tech stack and infrastructure is probably useless and a waste of time at the beginning.&lt;/p&gt;

&lt;p&gt;Try to divide the learning process in small chunks, if it's not really needed to  achieve the goal/s, it could probably wait, as understanding the entire architecture of your system is not really important at this stage.&lt;/p&gt;

&lt;p&gt;This was everything I had for the &lt;code&gt;Before the first day&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I am really looking forward to knowing how your onboarding process looks like, feel free to comment here or tweet &lt;a href="https://twitter.com/christianbarra"&gt;@christianbarra&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>teams</category>
      <category>onboarding</category>
      <category>tech</category>
      <category>wowexperience</category>
    </item>
    <item>
      <title>Python dictionary views</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Mon, 07 Jan 2019 21:27:16 +0000</pubDate>
      <link>https://forem.com/barrachri/python-dictionary-views-1jji</link>
      <guid>https://forem.com/barrachri/python-dictionary-views-1jji</guid>
      <description>&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G-3BiDB7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/Djbs5qOU0AAeRCg.png" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--z4azIggs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1020069861337239555/VBUEOUPa_normal.jpg" alt="Christian Barra profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Christian Barra
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @christianbarra
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B8bbACBj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-99c56e7c338b4d5c17d78f658882ddf18b0bbde5b3f42f84e7964689e7e8fb15.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Did you know that "dict.keys()" returns a view that is a set-like object? &lt;a href="https://twitter.com/hashtag/Python"&gt;#Python&lt;/a&gt; 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      12:12 PM - 31 Jul 2018
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1024266442374557696" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1024266442374557696" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      2
      &lt;a href="https://twitter.com/intent/like?tweet_id=1024266442374557696" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      5
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;Dictionary is one of the Python's greatest features and using the &lt;code&gt;keys()&lt;/code&gt;, &lt;code&gt;items()&lt;/code&gt; and &lt;code&gt;values()&lt;/code&gt; methods is really common.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;first_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&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="s"&gt;"b"&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;first_dictionary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&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="n"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Key {key} with value {value}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Key a with value 1
# Key b with value 2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But do you know which kind of object is returned?&lt;/p&gt;

&lt;p&gt;They all return a special object, called &lt;a href="https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects"&gt;view&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why are &lt;strong&gt;views&lt;/strong&gt; useful?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they provide a dynamic view on the underline object (you change the dictionary and the view will change as well)&lt;/li&gt;
&lt;li&gt;the object returned by &lt;code&gt;keys()&lt;/code&gt; and &lt;code&gt;items()&lt;/code&gt; behaves like a set-like object (with &lt;code&gt;items()&lt;/code&gt; when the pairs are &lt;a href="https://docs.python.org/3/glossary.html#term-hashable"&gt;hashable&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And being a set-like object means you can use the &lt;a href="https://docs.python.org/3.6/library/stdtypes.html#set-types-set-frozenset"&gt;set operations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's consider an example, where we want to find the common keys between 2 dictionaries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;first_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&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="s"&gt;"b"&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;second_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"b"&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="s"&gt;"c"&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;first_dictionary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;second_dictionary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# {'b'}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;amp;&lt;/code&gt; is the &lt;a href="https://docs.python.org/3.6/library/stdtypes.html#frozenset.intersection"&gt;intersection operator&lt;/a&gt; and returns the common elements between our dictionaries' keys in this case.&lt;/p&gt;

&lt;p&gt;What about the elements that are not in common?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;first_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&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="s"&gt;"b"&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;second_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"b"&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="s"&gt;"c"&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;first_dictionary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;second_dictionary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# {'a', 'c'}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is called &lt;a href="https://docs.python.org/3.6/library/stdtypes.html#frozenset.symmetric_difference"&gt;simmetric difference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One thing that you cannot do is change the dictionary while iterating over the view object.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;first_dictionary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;first_dictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&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="nb"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="n"&gt;during&lt;/span&gt; &lt;span class="n"&gt;iteration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>python</category>
      <category>tipstricks</category>
      <category>dictionary</category>
      <category>views</category>
    </item>
    <item>
      <title>Discovering the pathlib module</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Wed, 12 Dec 2018 04:49:37 +0000</pubDate>
      <link>https://forem.com/barrachri/discovering-the-pathlib-module-44g</link>
      <guid>https://forem.com/barrachri/discovering-the-pathlib-module-44g</guid>
      <description>&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1022436417887248384-223" src="https://platform.twitter.com/embed/Tweet.html?id=1022436417887248384"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1022436417887248384-223');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1022436417887248384&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.python.org/3/library/index.html" rel="noopener noreferrer"&gt;Python Standard Library&lt;/a&gt; is like a gold mine, and the &lt;a href="https://docs.python.org/3/library/pathlib.html#module-pathlib" rel="noopener noreferrer"&gt;pathlib&lt;/a&gt; module is really a gem.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pathlib&lt;/code&gt; provides an useful abstraction on top of different filesystems (posix and Windows).&lt;/p&gt;

&lt;p&gt;But that's just a small part of the very friendly user experience that you can have.&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;# files in my folder&lt;/span&gt;
README.md  example.py subfolder/README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can import &lt;code&gt;Path&lt;/code&gt; from &lt;code&gt;pathlib&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;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# PosixPath('.')
&lt;/span&gt;
&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterdir&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="c1"&gt;# [PosixPath('subfolder'), PosixPath('README.md'), PosixPath('example.py')]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Path()&lt;/code&gt; returns (in this case) a &lt;code&gt;pathlib.PosixPath&lt;/code&gt; object with a very handy &lt;code&gt;iterdir&lt;/code&gt;, a useful generator for when you have a lot of files in your folder.&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;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterdir&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;generator&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iterdir&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x10aca2cf0&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;Do you want to get only the files with a specific format?&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;md_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.md&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# [PosixPath('README.md')]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get a similar result using &lt;code&gt;glob&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;md_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="c1"&gt;# [PosixPath('README.md')]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;glob&lt;/code&gt; is quite powerful, using the &lt;code&gt;**&lt;/code&gt; pattern you can search recursively&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;md_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="c1"&gt;# [PosixPath('README.md'), PosixPath('subfolder/README.md')]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to learn more about the &lt;code&gt;pathlib&lt;/code&gt; module the &lt;a href="https://www.python.org/dev/peps/pep-0428/" rel="noopener noreferrer"&gt;PEP 428&lt;/a&gt; and the &lt;a href="https://docs.python.org/3/library/pathlib.html#module-pathlib" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; are the right places where to start.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tipstricks</category>
      <category>pathlib</category>
      <category>filesystem</category>
    </item>
    <item>
      <title>How to reverse a list in Python</title>
      <dc:creator>Christian Barra</dc:creator>
      <pubDate>Thu, 06 Dec 2018 08:23:47 +0000</pubDate>
      <link>https://forem.com/barrachri/how-to-reverse-a-list-in-python-1i97</link>
      <guid>https://forem.com/barrachri/how-to-reverse-a-list-in-python-1i97</guid>
      <description>&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1022074115476389888-885" src="https://platform.twitter.com/embed/Tweet.html?id=1022074115476389888"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1022074115476389888-885');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1022074115476389888&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Reversing a list in Python is really, really easy.&lt;/p&gt;

&lt;p&gt;This doesn't apply only to &lt;code&gt;list&lt;/code&gt; but to &lt;a href="https://docs.python.org/3/glossary.html#term-sequence" rel="noopener noreferrer"&gt;sequence&lt;/a&gt; in general (string and tuple for 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="n"&gt;msg&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 there&lt;/span&gt;&lt;span class="sh"&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;msg&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# ereht olleh
&lt;/span&gt;
&lt;span class="n"&gt;my_tuple&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_tuple&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# (3, 2, 1)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The secret behind this magic? The &lt;a href="https://docs.python.org/3/library/functions.html#slice" rel="noopener noreferrer"&gt;slice object&lt;/a&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;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;christian&lt;/span&gt;&lt;span class="sh"&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;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;# 'naitsirhc'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While old I really recommend reading the &lt;a href="https://docs.python.org/3/whatsnew/2.3.html?highlight=slice#extended-slices" rel="noopener noreferrer"&gt;Python 2.3 What's new&lt;/a&gt;, when the support for built-in types was introduced.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tipstricks</category>
      <category>list</category>
      <category>slice</category>
    </item>
  </channel>
</rss>
