<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Victor Silva</title>
    <description>The latest articles on DEV Community by Victor Silva (@victorosilva).</description>
    <link>https://dev.to/victorosilva</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%2F77881%2F5cfba1ba-080a-40c0-9323-22af9b06b321.jpg</url>
      <title>DEV Community: Victor Silva</title>
      <link>https://dev.to/victorosilva</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victorosilva"/>
    <language>en</language>
    <item>
      <title>Setup Ubuntu for Python development</title>
      <dc:creator>Victor Silva</dc:creator>
      <pubDate>Thu, 13 Jun 2019 01:58:29 +0000</pubDate>
      <link>https://dev.to/victorosilva/setup-ubuntu-for-python-development-34k2</link>
      <guid>https://dev.to/victorosilva/setup-ubuntu-for-python-development-34k2</guid>
      <description>&lt;p&gt;In this tutorial I'll show you how I prepare my machine for Python development after installing Ubuntu or one of its derivatives. There are many ways to do this, with different tools and techniques. Here I am sharing the way that works best for me.&lt;/p&gt;

&lt;p&gt;These are the two goals of this process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;not&lt;/strong&gt; to mess with the operating system's Python interpreter;&lt;/li&gt;
&lt;li&gt;be able to work on projects that use different Python versions and different Python libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a practical example, we will create three Python projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; project with Python 3.7;&lt;/li&gt;
&lt;li&gt;A &lt;a href="http://mezzanine.jupo.org/" rel="noopener noreferrer"&gt;Mezzanine&lt;/a&gt; project with Python 3.7 too;&lt;/li&gt;
&lt;li&gt;A &lt;a href="http://flask.pocoo.org/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; project with Python 2.7.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will begin with a fresh installation of Ubuntu 19.04.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  System dependencies
&lt;/h2&gt;

&lt;p&gt;We will build some Python versions, and for that we will need to install some system dependencies. We'll follow the recommendation of the subsection &lt;strong&gt;"Ubuntu/Debian/Mint"&lt;/strong&gt; of pyenv's &lt;a href="https://github.com/pyenv/pyenv/wiki#suggested-build-environment" rel="noopener noreferrer"&gt;wiki's "Suggested build environment" section&lt;/a&gt;. As of 2019-06-11, they are:&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev


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

&lt;/div&gt;

&lt;p&gt;So we open a Terminal (&lt;code&gt;Ctrl + Alt + T&lt;/code&gt;) and install the dependencies:&lt;/p&gt;

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

&lt;p&gt;We'll also need &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt;:&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;git


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

&lt;/div&gt;

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

&lt;h2&gt;
  
  
  Python version management
&lt;/h2&gt;

&lt;p&gt;After the system dependencies we'll install &lt;a href="https://github.com/pyenv/pyenv" rel="noopener noreferrer"&gt;pyenv&lt;/a&gt;. Among other things, pyenv lets us install different Python versions in our system.&lt;/p&gt;

&lt;p&gt;Below, we're following the &lt;a href="https://github.com/pyenv/pyenv#installation" rel="noopener noreferrer"&gt;"installation" section&lt;/a&gt; of pyenv's GitHub page. Be sure to check that page for updated instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone pyenv into &lt;code&gt;$HOME/.pyenv&lt;/code&gt;
&lt;/h3&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/pyenv/pyenv.git ~/.pyenv


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

&lt;/div&gt;

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

&lt;h3&gt;
  
  
  Update &lt;code&gt;$HOME/.bashrc&lt;/code&gt; with the lines below, and restart the shell
&lt;/h3&gt;

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

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
if command -v pyenv 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
  eval "$(pyenv init -)"
fi


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

&lt;/div&gt;

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

&lt;h3&gt;
  
  
  Install Python 2.7 and Python 3.7
&lt;/h3&gt;

&lt;p&gt;Note: these may take a long time to complete, for pyenv will download and build CPython from source for each version.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;pyenv &lt;span class="nb"&gt;install &lt;/span&gt;2.7.16
&lt;span class="nv"&gt;$ &lt;/span&gt;pyenv &lt;span class="nb"&gt;install &lt;/span&gt;3.7.3


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

&lt;/div&gt;

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

&lt;p&gt;As we can see in the screenshot, the Python versions have been installed in &lt;code&gt;$HOME/.pyenv/versions&lt;/code&gt;. We can run both interpreters directly to verify that they are working properly:&lt;/p&gt;

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

&lt;p&gt;However, we will not be installing libraries in these Python interpreters. They will be the &lt;em&gt;base&lt;/em&gt; interpreters for our projects. Actually, each project will have its own isolated Python environment, with its own Python interpreter and its own libraries. &lt;/p&gt;

&lt;h2&gt;
  
  
  Python isolated environments
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://virtualenv.pypa.io/en/latest/" rel="noopener noreferrer"&gt;virtualenv&lt;/a&gt; is a great tool for creating these isolated environments. Installing it on Ubuntu is straightforward:&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;virtualenv


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

&lt;/div&gt;

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

&lt;p&gt;The creation of the environments is easy too. We'll create them on the next sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Django project
&lt;/h2&gt;

&lt;p&gt;Let's create a directory &lt;code&gt;$HOME/projects/django_project&lt;/code&gt; and then create an isolated Python environment for this project in &lt;code&gt;$HOME/projects/django_project/.venv_django&lt;/code&gt; using the Python 3.7.3 that we installed earlier.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/projects
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/projects/django_project
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/django_project
&lt;span class="nv"&gt;$ &lt;/span&gt;virtualenv .venv_django &lt;span class="nt"&gt;--python&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.pyenv/versions/3.7.3/bin/python


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

&lt;/div&gt;

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

&lt;p&gt;In the screenshot, we can see that an environment was created in the new &lt;code&gt;.venv_django&lt;/code&gt; directory, and it contains its own Python interpreter, along with some other tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Activate the environment for the Django project
&lt;/h3&gt;

&lt;p&gt;We need to &lt;strong&gt;activate&lt;/strong&gt; this environment so we can use its Python interpreter and install libraries on it. After the activation, executing &lt;code&gt;python&lt;/code&gt; (the Python interpreter) or &lt;code&gt;pip&lt;/code&gt; (the Python package manager) will use the versions of these tools that are inside the environment.&lt;/p&gt;

&lt;p&gt;The activation is essential. If we skip it, we will end up messing with the operating system's Python interpreter, and that's exactly what we &lt;strong&gt;don't&lt;/strong&gt; want to do.&lt;/p&gt;

&lt;p&gt;To perform the environment activation, we need to &lt;code&gt;source&lt;/code&gt; the script &lt;code&gt;bin/activate&lt;/code&gt; located inside the environment directory.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv_django/bin/activate


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

&lt;/div&gt;

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

&lt;p&gt;Notice that, when the environment is activated, the prompt is prefixed with the name of the environment's directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Django and create and run the project in the environment
&lt;/h3&gt;

&lt;p&gt;Now, with the environment properly activated, we can install Django with &lt;a href="https://github.com/pypa/pip" rel="noopener noreferrer"&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt;, create the Django project and run it.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;Django
&lt;span class="nv"&gt;$ &lt;/span&gt;django-admin.py startproject django_project &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py runserver



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

&lt;/div&gt;

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

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

&lt;p&gt;To quit the Django development server, just hit &lt;code&gt;Ctrl + C&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we will create a Python environment for a Mezzanine project, so we must deactivate the currently activated environment (the Django one) and leave the Django project directory:&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;deactivate
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..


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

&lt;/div&gt;

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

&lt;h2&gt;
  
  
  Create the Mezzanine project
&lt;/h2&gt;

&lt;p&gt;In a similar fashion, now we will&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create the environment for the Mezzanine project (based on Python 3.7.3 again);&lt;/li&gt;
&lt;li&gt;activate the environment;&lt;/li&gt;
&lt;li&gt;install the libraries that we need;&lt;/li&gt;
&lt;li&gt;create and run the project.&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;mezzanine_project
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;mezzanine_project
&lt;span class="nv"&gt;$ &lt;/span&gt;virtualenv .venv_mezzanine &lt;span class="nt"&gt;--python&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.pyenv/versions/3.7.3/bin/python
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv_mezzanine/bin/activate
&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mezzanine
&lt;span class="nv"&gt;$ &lt;/span&gt;mezzanine-project myproject &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py createdb
&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py runserver


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

&lt;/div&gt;

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

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

&lt;p&gt;To quit the Django development server (Mezzanine is built on top of Django), just hit &lt;code&gt;Ctrl + C&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;Before moving to the last project (the Flask one), let's deactivate the Mezzanine environment and leave the project directory:&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;deactivate
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..


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

&lt;/div&gt;

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

&lt;h2&gt;
  
  
  Create the Flask project
&lt;/h2&gt;

&lt;p&gt;For the Flask project, the process will be similar, but the environment will be based on Python 2.7.16 instead. Flask doesn't have an autogenerator of basic projects like Django and Mezzanine do, therefore we'll need to write a little bit of code in order to have a minimal Flask application.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;flask_project
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;flask_project
&lt;span class="nv"&gt;$ &lt;/span&gt;virtualenv .venv_flask &lt;span class="nt"&gt;--python&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.pyenv/versions/2.7.16/bin/python
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv_flask/bin/activate
&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;app.py


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

&lt;/div&gt;

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

&lt;p&gt;Let's enter the following code in the file &lt;code&gt;$HOME/projects/flask_project/app.py&lt;/code&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;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&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="nd"&gt;@app.route&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello, World&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;p&gt;Now we can run the Flask app to make sure that it works as expected.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;flask run


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

&lt;/div&gt;

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

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

&lt;p&gt;To quit the Flask development server, just hit &lt;code&gt;Ctrl + C&lt;/code&gt;. Then, let's deactivate the environment that we created for the Flask project and leave its directory.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;deactivate
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..


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

&lt;/div&gt;

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

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

&lt;ul&gt;
&lt;li&gt;We use &lt;strong&gt;Pyenv&lt;/strong&gt; to install different Python interpreters&lt;/li&gt;
&lt;li&gt;We use &lt;strong&gt;virtualenv&lt;/strong&gt; to create isolated environments for different Python projects&lt;/li&gt;
&lt;li&gt;We &lt;strong&gt;activate&lt;/strong&gt; each environment before installing the libraries they need&lt;/li&gt;
&lt;li&gt;When an environment is activated, the prompt is prefixed with the name of the environment's directory&lt;/li&gt;
&lt;li&gt;We &lt;strong&gt;deactivate&lt;/strong&gt; an environment when we don't need to use it&lt;/li&gt;
&lt;li&gt;We never touch the operating system's Python interpreter&lt;/li&gt;
&lt;li&gt;Each environment has its own isolated set of installed libraries, as shown below:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;That's it! Do you have a different approach which works best for you?&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>My case against the "ternary operator" in Python</title>
      <dc:creator>Victor Silva</dc:creator>
      <pubDate>Sun, 19 May 2019 23:45:25 +0000</pubDate>
      <link>https://dev.to/victorosilva/my-case-against-the-ternary-operator-in-python-3068</link>
      <guid>https://dev.to/victorosilva/my-case-against-the-ternary-operator-in-python-3068</guid>
      <description>&lt;h2&gt;
  
  
  Ternary what?
&lt;/h2&gt;

&lt;p&gt;Ternary operators, also known as &lt;a href="https://docs.python.org/3/reference/expressions.html#conditional-expressions" rel="noopener noreferrer"&gt;conditional expressions&lt;/a&gt; in Python, can be used to make our code shorter:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;But i don't like them for two reasons:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. They often go against the Zen of Python
&lt;/h2&gt;

&lt;p&gt;If you don't know the &lt;a href="https://www.python.org/dev/peps/pep-0020/" rel="noopener noreferrer"&gt;Zen of Python&lt;/a&gt;, start a Python interpreter and type &lt;code&gt;import this&lt;/code&gt;. The Zen is a set of guiding principles for writing Python code.&lt;/p&gt;

&lt;p&gt;Arguably, most of the time conditional expressions are likely to break 3 of these principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beautiful is better than ugly.

&lt;ul&gt;
&lt;li&gt;E.g.: &lt;code&gt;'Odd' if n % 2 else 'Even'&lt;/code&gt; is anything but beautiful&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Sparse is better than dense.

&lt;ul&gt;
&lt;li&gt;E.g.: &lt;code&gt;'Odd' if n % 2 else 'Even'&lt;/code&gt; is more dense than an if/else statement&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Readability counts.

&lt;ul&gt;
&lt;li&gt;E.g.: &lt;code&gt;'Odd' if n % 2 else 'Even'&lt;/code&gt; is not as readable as an if/else statement&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;And, more importantly...&lt;/p&gt;

&lt;h2&gt;
  
  
  2. They often trick our test coverage
&lt;/h2&gt;

&lt;p&gt;Conditional expressions may make us think that we've covered all possible branches with tests when we actually have not. Check the following example:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;If I run the tests with &lt;a href="https://coverage.readthedocs.io" rel="noopener noreferrer"&gt;coverage&lt;/a&gt;, this is the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tzdwx0t74u64f4400k4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tzdwx0t74u64f4400k4.png" width="643" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the image, I covered 100% of the lines, without missing any branch. But looking again at the code, we can see that there is no test for months of the second semester of the year.&lt;/p&gt;

&lt;p&gt;If I use the classic if statement instead, the lack of coverage will be correctly detected:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q3035utnckv731b6cyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q3035utnckv731b6cyy.png" width="643" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I run the coverage exporting the results to HTML, I can actually see which statement was skipped and which branch was partially run during the test:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F207q7vkcsx7bk6o11uti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F207q7vkcsx7bk6o11uti.png" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4otnb7su8wr4azazfzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4otnb7su8wr4azazfzu.png" width="581" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I know that I should write a test that calls &lt;code&gt;semester&lt;/code&gt; with a value greater than 6 if I want the function to be 100% covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;These are the reasons why I avoid conditional expressions and advise people to avoid them too: they tend to break the Zen of Python and they can fool our code coverage tools.&lt;/p&gt;

&lt;p&gt;Granted, not every piece of code does need to be 100% covered by tests, but the ones that do should be measurable in the best way possible, and we've just seen that conditional expressions can get in our way when we are pursuing that.&lt;/p&gt;

</description>
      <category>python</category>
      <category>python3</category>
    </item>
    <item>
      <title>Make simple Python types rich</title>
      <dc:creator>Victor Silva</dc:creator>
      <pubDate>Fri, 17 May 2019 23:42:23 +0000</pubDate>
      <link>https://dev.to/victorosilva/explicit-is-better-than-implicit-make-simples-types-rich-11i</link>
      <guid>https://dev.to/victorosilva/explicit-is-better-than-implicit-make-simples-types-rich-11i</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/victorosilva/explicit-is-better-than-implicit-avoid-dict-overuse-57k4"&gt;previous post of this series&lt;/a&gt;, I created a fictional example that uses a dict to map the installments of a loan to their corresponding due dates, in the following structure:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Suppose that we pass that dict all over the place in our code, and that in some places we need to calculate the difference between the first and the last due date.&lt;br&gt;
We could create a function that makes the calculation, and use it wherever we need it, right? And we could be nice to the callers by providing helpful type hints, of course:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;But now we have to import that function in every place we deal with the dict and need the difference between the dates. It would be good if we could encapsulate both the function and the data in the same object without having to create a complex class.&lt;br&gt;
Well, it turns out that we can do that by subclassing Python's built-in dict, and our subclass will behave like a standard dict because it will actually &lt;strong&gt;be&lt;/strong&gt; one:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Note that, unless we read the class's code, we have no clue that the keys and values are supposed to be integers and dates respectively. Not even PyCharm can help us:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kkv9FGFA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fqdrurmneghqqjzn7cv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kkv9FGFA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fqdrurmneghqqjzn7cv8.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can fix that by inheriting from &lt;code&gt;typing.Dict&lt;/code&gt; instead, which is a generic version of dict that can be annotated:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Look at the autocomplete that we have now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5ofCK0z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9kctgg5kroli7jaz7jei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5ofCK0z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9kctgg5kroli7jaz7jei.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

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

</description>
      <category>python</category>
      <category>python3</category>
    </item>
    <item>
      <title>Git surgery #1</title>
      <dc:creator>Victor Silva</dc:creator>
      <pubDate>Sun, 28 Apr 2019 17:39:36 +0000</pubDate>
      <link>https://dev.to/victorosilva/git-surgery-1-51m0</link>
      <guid>https://dev.to/victorosilva/git-surgery-1-51m0</guid>
      <description>&lt;h2&gt;
  
  
  The context
&lt;/h2&gt;

&lt;p&gt;At work we use Vincent Driessen's &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/" rel="noopener noreferrer"&gt;Git Flow&lt;/a&gt; branching model. Recently we found ourselves in the following situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;feature &lt;strong&gt;A&lt;/strong&gt; had been finished and merged into &lt;code&gt;develop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;release &lt;code&gt;1.2.3&lt;/code&gt;, containing feature &lt;strong&gt;A&lt;/strong&gt;, had been started&lt;/li&gt;
&lt;li&gt;feature &lt;strong&gt;B&lt;/strong&gt; had been started &lt;em&gt;after&lt;/em&gt; the start of the release &lt;code&gt;1.2.3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;there were some adjustment commits for feature &lt;strong&gt;A&lt;/strong&gt; in the release &lt;code&gt;1.2.3&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;feature &lt;strong&gt;B&lt;/strong&gt; had been finished and merged into &lt;code&gt;develop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;feature &lt;strong&gt;B&lt;/strong&gt; didn't depend on any code added by feature &lt;strong&gt;A&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visually, that's what we had (each column represents a branch; and each point represents a commit, with a single letter instead of its hash):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F26jgkkdkvn5fiht9iofg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F26jgkkdkvn5fiht9iofg.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we just had to test and finish the release &lt;code&gt;1.2.3&lt;/code&gt; (merge it into &lt;code&gt;master&lt;/code&gt;, create a tag &lt;code&gt;1.2.3&lt;/code&gt; at the merge, and then merge the tag into &lt;code&gt;develop&lt;/code&gt; in order to integrate commits &lt;code&gt;F&lt;/code&gt;, &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;J&lt;/code&gt;) and deploy the newly created tag into production. Then we would create another release for feature &lt;strong&gt;B&lt;/strong&gt; (probably &lt;code&gt;1.2.4&lt;/code&gt;), test, finish and deploy it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;For reasons that are beside the point of this post, it was decided that feature &lt;strong&gt;B&lt;/strong&gt; should make it into production &lt;em&gt;before&lt;/em&gt; feature &lt;strong&gt;A&lt;/strong&gt;. In order to not mess up our version numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;release &lt;code&gt;1.2.3&lt;/code&gt; now should contain feature &lt;strong&gt;B&lt;/strong&gt;, but

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;without&lt;/em&gt; the code of feature &lt;strong&gt;A&lt;/strong&gt; commits (&lt;em&gt;B&lt;/em&gt;, &lt;em&gt;C&lt;/em&gt;, &lt;em&gt;D&lt;/em&gt; and &lt;em&gt;E&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;without&lt;/em&gt; the code of the adjustment commits &lt;em&gt;F&lt;/em&gt;, &lt;em&gt;G&lt;/em&gt; and &lt;em&gt;J&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;the "new" release &lt;code&gt;1.2.3&lt;/code&gt; should be tested, finished and deployed&lt;/li&gt;

&lt;li&gt;feature &lt;strong&gt;A&lt;/strong&gt; and its adjustment commits should be in a new release (&lt;code&gt;1.2.4&lt;/code&gt;)&lt;/li&gt;

&lt;li&gt;commit history of &lt;code&gt;develop&lt;/code&gt; should &lt;strong&gt;not&lt;/strong&gt; be rewritten, for it's a shared branch and other features may have been started based on it.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Note: for simplicity, in the Git commands below I'll use the letter that was used to represent each commit in the images instead of the commits' hashes.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We took note of the hashes of the feature &lt;strong&gt;A&lt;/strong&gt; adjustment commits (&lt;em&gt;F&lt;/em&gt;, &lt;em&gt;G&lt;/em&gt; and &lt;em&gt;J&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On branch &lt;code&gt;develop&lt;/code&gt;, &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_reverse_commit" rel="noopener noreferrer"&gt;we reverted the commit that merged &lt;code&gt;feature/A&lt;/code&gt; into &lt;code&gt;develop&lt;/code&gt;&lt;/a&gt; (the merge commit &lt;em&gt;E&lt;/em&gt;), so that code related to feature &lt;strong&gt;A&lt;/strong&gt; was "removed" from &lt;code&gt;develop&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;master&amp;gt; $ git checkout develop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;develop&amp;gt; $ git revert -m 1 E  # creates commit M&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9uuealxqegrlh9cecdrb.jpg"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We used &lt;code&gt;reset --hard&lt;/code&gt; to reset the branch &lt;code&gt;release/1.2.3&lt;/code&gt; to &lt;code&gt;develop&lt;/code&gt;, so that &lt;code&gt;release/1.2.3&lt;/code&gt; pointed to commit &lt;em&gt;M&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;develop&amp;gt; $ git checkout release/1.2.3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;release/1.2.3&amp;gt; $ git reset --hard develop&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Still on &lt;code&gt;release/1.2.3&lt;/code&gt;, we made a commit to bump the version number (a change &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/#creating-a-release-branch" rel="noopener noreferrer"&gt;required by Git Flow&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;release/1.2.3&amp;gt; $ echo '1.2.3' &amp;gt; version.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;release/1.2.3&amp;gt; $ git add version.txt &amp;amp;&amp;amp; git commit -m "Bump version number"  # creates commit N&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F31ggvxrstvsugoond64c.jpg"&gt;
Note that, since the &lt;code&gt;reset --hard&lt;/code&gt;, the commits &lt;em&gt;F&lt;/em&gt;, &lt;em&gt;G&lt;/em&gt; and &lt;em&gt;J&lt;/em&gt; have been "lost". That's why we took note of their hashes in the beggining - we'll bring them back later.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After testing release &lt;code&gt;1.2.3&lt;/code&gt;, which now only contained code related to feature &lt;strong&gt;B&lt;/strong&gt;, we finished (creating its tag and merging the tag into &lt;code&gt;develop&lt;/code&gt;) and deployed it&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;release/1.2.3&amp;gt; $ git checkout master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;master&amp;gt; $ git merge --no-ff release/1.2.3  # creates commit O&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;master&amp;gt; $ git tag -a 1.2.3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;./fictional-deploy.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;master&amp;gt; $ git checkout develop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;develop&amp;gt; $ git merge --no-ff 1.2.3  # creates commit P&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flatzqfbtzmzo8v6i0r3b.jpg"&gt;
At this point, we have released only feature &lt;strong&gt;B&lt;/strong&gt;, but now we must bring back the code of feature &lt;strong&gt;A&lt;/strong&gt;, create a new release with it, and also bring back its adjustments commits.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On develop, we reverted the commit &lt;em&gt;M&lt;/em&gt;, which was a revert commit itself. Yes, we "reverted a revert".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;develop&amp;gt; $ git revert M  # creates commit Q&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1joeg4k6mnmi6q0hm7gy.jpg"&gt;
In practice, that brought the code of feature &lt;strong&gt;A&lt;/strong&gt; (inserted in commits &lt;em&gt;B&lt;/em&gt;, &lt;em&gt;C&lt;/em&gt;, &lt;em&gt;D&lt;/em&gt; and &lt;em&gt;E&lt;/em&gt;) back to develop.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, we created the new release (&lt;code&gt;1.2.4&lt;/code&gt;) and brought the adjustments previously made to feature &lt;strong&gt;A&lt;/strong&gt; into it&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;develop&amp;gt; $ git checkout -b release/1.2.4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;release/1.2.4&amp;gt; $ git cherry-pick F  # creates commit R&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;release/1.2.4&amp;gt; $ git cherry-pick G  # creates commit S&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;release/1.2.4&amp;gt; $ git cherry-pick J  # creates commit T&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Folg2dr23ost655l0rlze.jpg"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With a few basic Git commands (&lt;code&gt;reset&lt;/code&gt;, &lt;code&gt;revert&lt;/code&gt;, &lt;code&gt;cherry-pick&lt;/code&gt;, etc.) we were able to solve our problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;only code related to feature &lt;strong&gt;B&lt;/strong&gt; has been deployed&lt;/li&gt;
&lt;li&gt;feature &lt;strong&gt;A&lt;/strong&gt; and its adjustment commits have not been lost&lt;/li&gt;
&lt;li&gt;we haven't messed up our versioning numbers&lt;/li&gt;
&lt;li&gt;the history of the branch &lt;code&gt;develop&lt;/code&gt; has not been rewritten.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/a0h7sAqON67nO/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/a0h7sAqON67nO/giphy.gif" alt="Success!"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>gitcommands</category>
      <category>gitflow</category>
    </item>
    <item>
      <title>Avoid dict overuse in Python</title>
      <dc:creator>Victor Silva</dc:creator>
      <pubDate>Tue, 14 Aug 2018 04:02:07 +0000</pubDate>
      <link>https://dev.to/victorosilva/explicit-is-better-than-implicit-avoid-dict-overuse-57k4</link>
      <guid>https://dev.to/victorosilva/explicit-is-better-than-implicit-avoid-dict-overuse-57k4</guid>
      <description>&lt;p&gt;Python's dicts are great and we all love them, but on large projects we can easily end up with dozens of functions like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;There's nothing inherently wrong with the function above, but there are some little things that may complicate our lives as the project grows.&lt;/p&gt;

&lt;p&gt;We may mistype the value of a key when accessing the returned data (like typing &lt;code&gt;daily_interest_rates&lt;/code&gt; instead of &lt;code&gt;daily_interest_rate&lt;/code&gt;). Because we're dealing with a dict, we would only find this error (that is, &lt;strong&gt;if&lt;/strong&gt; we find it) during run time.&lt;/p&gt;

&lt;p&gt;Or maybe we could start calling this function and using its returned dict in a lot of places. In this case, we would find ourselves repeatedly coming back to the implementation just to remember the dict's keys, or to check the due dates' structure.&lt;/p&gt;

&lt;p&gt;We can get rid of these potential problems in advance, and make this code a lot more explicit, if we&lt;/p&gt;

&lt;h2&gt;
  
  
  Use proper classes instead of using dicts everywhere
&lt;/h2&gt;

&lt;p&gt;Let's try it:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: below, I will use &lt;a href="https://docs.python.org/3/library/dataclasses.html" rel="noopener noreferrer"&gt;&lt;code&gt;dataclasses&lt;/code&gt;&lt;/a&gt;, added in Python 3.7, to avoid some boilerplate __init__ code.&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now our function returns instances of a class, and thanks to the type hints that were added, we know the exact structure of these instances.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: if you never heard of type hints, check my really short introdution to them:&lt;/em&gt; &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/victorosilva" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F77881%2F5cfba1ba-080a-40c0-9323-22af9b06b321.jpg" alt="victorosilva"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/victorosilva/explicit-is-better-than-implicit-type-hints-12i0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Python's type hints&lt;/h2&gt;
      &lt;h3&gt;Victor Silva ・ Aug 12 '18&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#python3&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typehint&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/blockquote&gt;

&lt;p&gt;By using any Python-aware IDE, the two problems that I described before are no more. Because of the new class &lt;code&gt;CalculationResult&lt;/code&gt; combined with type hints, &lt;a href="https://www.jetbrains.com/pycharm/" rel="noopener noreferrer"&gt;PyCharm&lt;/a&gt; will warn me if I mistype a field name:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fommx23rjl6mb00lpq9sl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fommx23rjl6mb00lpq9sl.png" alt="PyCharm - mistyped field" width="587" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I forget the structure of the due dates, type hints got me covered. PyCharm will rely on them to remind me that the dates are the values of a dict whose keys are integers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7u10t0a9ukjd7xe1dsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7u10t0a9ukjd7xe1dsb.png" alt="PyCharm - field structure" width="654" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I'm not saying we should stop using dicts altogether. My point is that, for larger projects, maybe it's better to represent our data as proper classes. With the new Python dataclasses, we can do this in a jiffy.&lt;/p&gt;

&lt;p&gt;Nowadays I tend to prefer dicts only in places like the field &lt;code&gt;CalculationResult.due_dates&lt;/code&gt; above: when all keys are of the same type and represent the same thing (e.g.: the installments) and all the values are of the same type and represent the same thing (e.g.: the due date of the corresponding installment).&lt;/p&gt;

&lt;p&gt;I find that my code is getting more expressive, explicit and maintainable by adopting this strategy.&lt;/p&gt;

</description>
      <category>python</category>
      <category>python3</category>
    </item>
    <item>
      <title>Python's type hints</title>
      <dc:creator>Victor Silva</dc:creator>
      <pubDate>Sun, 12 Aug 2018 02:12:26 +0000</pubDate>
      <link>https://dev.to/victorosilva/explicit-is-better-than-implicit-type-hints-12i0</link>
      <guid>https://dev.to/victorosilva/explicit-is-better-than-implicit-type-hints-12i0</guid>
      <description>&lt;p&gt;If you've been working with Python for a little time, chances are that you've heard that &lt;a href="https://www.python.org/dev/peps/pep-0020/" rel="noopener noreferrer"&gt;explicit is better than implicit&lt;/a&gt;. If you've been working with Python for a long time, chances are that you strongly agree with that statement.&lt;/p&gt;

&lt;p&gt;Today, I wanna share with you one of my favorite ways to make my Python code more explicit:&lt;/p&gt;

&lt;h2&gt;
  
  
  Use type hints
&lt;/h2&gt;

&lt;p&gt;Let's consider the following function:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Before reading its body (which could be extremely complex), one knows neither the expected parameter type nor the return type.&lt;/p&gt;

&lt;p&gt;Of course, we could name the function &lt;code&gt;get_due_dates_dict&lt;/code&gt; and the parameter &lt;code&gt;start_date&lt;/code&gt;, but that would be extremely anti-pythonic, specially when we have a much better option: &lt;a href="https://docs.python.org/3/library/typing.html" rel="noopener noreferrer"&gt;type hinting&lt;/a&gt; via &lt;a href="https://www.python.org/dev/peps/pep-3107/" rel="noopener noreferrer"&gt;function annotations&lt;/a&gt;. Check it out:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Ok, now by just looking at the function's heading, we know that it expects a date and returns a dict. And just as important, any Python-aware IDE will know this as well, and will give us some nice autocompletion features.&lt;/p&gt;

&lt;p&gt;But we can do better. We can annotate our function is such a way that we (and our IDEs) know even the structure of its return without having to read its body:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, it's explicit that our function returns a dict in which the keys are integers and the values are dates. In the screenshots below, we can see that &lt;a href="https://www.jetbrains.com/pycharm/" rel="noopener noreferrer"&gt;PyCharm&lt;/a&gt; understands this perfectly, and gives us precise tips and autocompletions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ulg9sdriri7jydal4rj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ulg9sdriri7jydal4rj.png" alt="PyCharm - parameter" width="494" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi6blg3nmdydv3nk1fmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi6blg3nmdydv3nk1fmj.png" alt="PyCharm - return" width="648" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Type hints make everyone's life easier down the road.&lt;/p&gt;

&lt;p&gt;That's it for today, happy coding!&lt;/p&gt;

</description>
      <category>python</category>
      <category>python3</category>
      <category>typehint</category>
    </item>
  </channel>
</rss>
