<?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: Meredydd Luff</title>
    <description>The latest articles on DEV Community by Meredydd Luff (@meredydd).</description>
    <link>https://dev.to/meredydd</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%2F500164%2Fabaf2a54-1074-4793-84e6-6a3e30fef4aa.jpg</url>
      <title>DEV Community: Meredydd Luff</title>
      <link>https://dev.to/meredydd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/meredydd"/>
    <language>en</language>
    <item>
      <title>Turn a Jupyter Notebook into a Web App</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Tue, 04 May 2021 16:14:27 +0000</pubDate>
      <link>https://dev.to/meredydd/turn-a-jupyter-notebook-into-a-web-app-lj3</link>
      <guid>https://dev.to/meredydd/turn-a-jupyter-notebook-into-a-web-app-lj3</guid>
      <description>&lt;p&gt;Let’s say you’re a data scientist, and you’ve been asked to solve a problem. Of course, what you really want is to build an interactive tool, so your colleagues can solve the problem themselves!&lt;/p&gt;

&lt;p&gt;In this tutorial, I'll show you how to take a machine-learning model in a Jupyter notebook, and &lt;strong&gt;turn it into a web application&lt;/strong&gt; using the &lt;a href="https://anvil.works/docs/uplink?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Anvil Uplink&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's what we'll do: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up a Jupyter notebook&lt;/li&gt;
&lt;li&gt;Connect it to an &lt;a href="https://anvil.works?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Anvil app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Make a user interface&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting up the Jupyter Notebook
&lt;/h2&gt;

&lt;p&gt;We'll start with a &lt;a href="https://www.kaggle.com/uysimty/keras-cnn-dog-or-cat-classification" rel="noopener noreferrer"&gt;pre-existing Jupyter Notebook&lt;/a&gt; containing a classification model that distinguishes between cats and dogs. You give it an image and it scores it as ‘cat’ or ‘dog’.&lt;/p&gt;

&lt;p&gt;(Thanks to &lt;a href="https://uysim.com/" rel="noopener noreferrer"&gt;Uysim Ty&lt;/a&gt; for sharing it on Kaggle.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the Uplink
&lt;/h2&gt;

&lt;p&gt;We'll use the &lt;a href="https://anvil.works/docs/uplink?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Anvil Uplink&lt;/a&gt; to connect a Jupyter Notebook to Anvil. It’s a library you can &lt;code&gt;pip install&lt;/code&gt; on your computer or wherever your Notebook is running.&lt;/p&gt;

&lt;p&gt;It allows you to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;call functions in your Jupyter Notebook from your Anvil app&lt;/li&gt;
&lt;li&gt;call functions in your Anvil app from your Juypter Notebook - store data in your Anvil app from your Jupyter Notebook &lt;/li&gt;
&lt;li&gt;use the &lt;a href="https://anvil.works/docs/server" rel="noopener noreferrer"&gt;Anvil server library&lt;/a&gt; inside your Jupyter Notebook&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works for any Python process - this happens to be a Jupyter Notebook, but it could be an ordinary Python script, a Flask app, even the Python REPL!&lt;/p&gt;

&lt;p&gt;To connect our notebook, we'll first need to enable the Uplink in the &lt;a href="https://anvil.works/?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Anvil IDE&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%2Fanvil.works%2Fdocs%2Fuplink%2Fimg%2Fuplink-dialog-location.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%2Fanvil.works%2Fdocs%2Fuplink%2Fimg%2Fuplink-dialog-location.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives us a key that we can then use in our code.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fjupyter-notebook-to-web-app%2Fenable-uplink.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fjupyter-notebook-to-web-app%2Fenable-uplink.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then need to &lt;code&gt;pip install&lt;/code&gt; the Uplink library on the machine the Jupyter Notebook is running on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install anvil-uplink
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding the following lines to our Jupyter notebook, we can connect it to our Anvil app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anvil.server&lt;/span&gt;

&lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR-UPLINK-KEY&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can do anything in our Jupyter Notebook that we can do in an Anvil &lt;a href="https://anvil.works/docs/server?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Server Module&lt;/a&gt; - call Anvil server functions, store data in &lt;a href="https://anvil.works/docs/data-tables?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Data Tables&lt;/a&gt;, and define server functions to be called from other parts of the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading an image into the Notebook
&lt;/h2&gt;

&lt;p&gt;We'll load an image into the Jupyter Notebook by making an &lt;code&gt;anvil.server.callable&lt;/code&gt; function in the Jupyter Notebook. It will classify the input image as either a cat or a dog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anvil.media&lt;/span&gt;

&lt;span class="nd"&gt;@anvil.server.callable&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TempFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&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;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_img&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re passing the image in as an Anvil &lt;a href="https://anvil.works/docs/media?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Media object&lt;/a&gt;, which we then write to a temporary file.&lt;/p&gt;

&lt;p&gt;The load_img function loads the file into &lt;a href="https://python-pillow.org/" rel="noopener noreferrer"&gt;Pillow&lt;/a&gt;, a Python imaging library.&lt;/p&gt;

&lt;p&gt;Then we can do a bit of post-processing of the image to get it into a format that the model likes:&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;# Inside the classify_image function
&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;resample&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BICUBIC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;img_to_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&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;arr&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can just pass it into our model and return the result:&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;# Inside the classify_image function
&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dog&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building a User Interface
&lt;/h2&gt;

&lt;p&gt;We can &lt;a href="https://anvil.works/articles/drag-and-drop-builder?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;drag-and-drop components&lt;/a&gt; to create a User Interface. It consists of a &lt;a href="https://anvil.works/docs/client/components/basic#fileloader?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;FileLoader&lt;/a&gt; to upload the images, an &lt;a href="https://anvil.works/docs/client/components/basic#image?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Image&lt;/a&gt; to display them, and a &lt;a href="https://anvil.works/docs/client/components/basic#label?utm_source=crosspost:dev.to:/learn/tutorials/jupyter-notebook-to-web-app"&gt;Label&lt;/a&gt; to display the classification.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fjupyter-notebook-to-web-app%2Fcreate-ui-drag-and-drop.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fjupyter-notebook-to-web-app%2Fcreate-ui-drag-and-drop.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the UI call the Notebook
&lt;/h2&gt;

&lt;p&gt;Now we need to write some Python code that runs in the browser so the app responds when an image is loaded in.&lt;/p&gt;

&lt;p&gt;We can define an event handler that triggers when the FileLoader gets a new file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;file_loader_1_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;event_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;This method is called when a new file is loaded into this FileLoader.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;classify_image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result_lbl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%s (%0.2f)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line calls the &lt;code&gt;classify_image&lt;/code&gt; function in the Jupyter Notebook, passing in the image file.&lt;/p&gt;

&lt;p&gt;Then we display the result (cat or dog) and the score (0 to 1; completely dog or completely cat).&lt;/p&gt;

&lt;p&gt;We also put the image file into the Image component so that the user can see their cat or dog (or other cat-or-dog-like image) and decide if they agree with the result.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fjupyter-notebook-to-web-app%2Fits-a-cat.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fjupyter-notebook-to-web-app%2Fits-a-cat.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your app is already published at a private URL, but we can give it a public URL. You can check out this finished app at &lt;a href="https://cat-or-dog.anvil.app" rel="noopener noreferrer"&gt;https://cat-or-dog.anvil.app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check out the whole tutorial:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/yh0B4HjQxOU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>7 Great Plotting Libraries for Python - Compared (with guides for each)</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Thu, 25 Mar 2021 17:56:22 +0000</pubDate>
      <link>https://dev.to/meredydd/7-great-plotting-libraries-for-python-compared-with-guides-for-each-3047</link>
      <guid>https://dev.to/meredydd/7-great-plotting-libraries-for-python-compared-with-guides-for-each-3047</guid>
      <description>&lt;h1&gt;
  
  
  How do I make plots in Python?
&lt;/h1&gt;

&lt;p&gt;This question used to have a simple answer: Matplotlib was the only way. Nowadays, Python is the language of Data Science and there’s a lot more choice. Which should you use?&lt;/p&gt;

&lt;p&gt;This guide will help you decide. I’ll show you how to use &lt;strong&gt;seven Python plotting libraries&lt;/strong&gt;, with a bonus &lt;strong&gt;in-depth guide for each library&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;I've also packaged an example for each library as an &lt;a href="https://anvil.works?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Anvil&lt;/a&gt; app, showing how to build &lt;strong&gt;web-based data apps with nothing but Python&lt;/strong&gt;. All these libraries are available in Anvil’s Server Modules, and Plotly works directly in Anvil’s &lt;a href="https://anvil.works/articles/python-gui-builder-web?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;front-end Python code&lt;/a&gt; too! Click through to the in-depth guides for sample code.&lt;/p&gt;

&lt;p&gt;The most popular Python plotting libraries are Matplotlib, Plotly, Seaborn, and Bokeh. I’ve also included some underrated gems that you should definitely consider: Altair, with its expressive API, and Pygal, with its beautiful SVG output. We’ll also look at the very convenient plotting API provided by Pandas.&lt;/p&gt;




&lt;h1&gt;
  
  
  Our Example Plot
&lt;/h1&gt;

&lt;p&gt;Each of these libraries takes a slightly different approach. To compare them, I'm going to make the same plot with each library, and show you the source code. I’ve chosen a grouped bar chart of British election results since 1966. Here it is:&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fplotting-in-anvil.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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fplotting-in-anvil.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I compiled a dataset of British election history &lt;a href="https://en.wikipedia.org/wiki/United_Kingdom_general_elections_overview" rel="noopener noreferrer"&gt;from Wikipedia&lt;/a&gt;: the number of seats in the UK parliament won by the Conservative, Labour, and Liberal parties (broadly defined) in each election from 1966 to 2019, plus the number of seats won by ‘others’. You can download it as a CSV file &lt;a href="https://anvil.works/blog/img/plotting-in-python/uk-election-results.csv" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Matplotlib
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://anvil.works/blog/plotting-in-matplotlib?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Matplotlib&lt;/a&gt; is the oldest Python plotting library, and it’s still the most popular. It was created in 2003 as part of the &lt;a href="https://www.scipy.org/about.html" rel="noopener noreferrer"&gt;SciPy Stack&lt;/a&gt;, an open-source scientific computing library similar to Matlab.&lt;/p&gt;

&lt;p&gt;Matplotlib gives you precise control over your plots – for example, you can define the individual x-position of each bar in your barplot. I’ve written a more detailed guide to Matplotlib, and you can find it &lt;a href="https://anvil.works/blog/plotting-in-matplotlib?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;here&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fmatplotlib.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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fmatplotlib.png" alt="image"&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wide&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

    &lt;span class="c1"&gt;# Initialise a figure. subplots() with no args gives one plot.
&lt;/span&gt;    &lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subplots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# A little data preparation
&lt;/span&gt;    &lt;span class="n"&gt;years&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;years&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Plot each bar plot. Note: manually calculating the 'dodges' of the bars
&lt;/span&gt;    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&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;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#0343df&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&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;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#e50000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&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;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#ffff14&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&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;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#929591&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Customise some display properties
&lt;/span&gt;    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Seats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UK election results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_xticks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# This ensures we have one tick per year, otherwise we get fewer
&lt;/span&gt;    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_xticklabels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;years&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vertical&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Ask Matplotlib to show the plot
&lt;/span&gt;    &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/blog/plotting-in-matplotlib?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Full Guide &amp;amp; Example Code: Plotting in Matplotlib &amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h1&gt;
  
  
  Seaborn
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://anvil.works/blog/plotting-in-seaborn?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Seaborn&lt;/a&gt; is an abstraction layer on top of Matplotlib - it gives you a really neat interface to make a wide range of useful plot types very easily.&lt;/p&gt;

&lt;p&gt;It doesn’t compromise on power, though! Seaborn gives you &lt;a href="https://anvil.works/blog/escape-hatches-and-ejector-seats?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;escape hatches&lt;/a&gt; to access the underlying Matplotlib objects, so you still have complete control. You can check out a more detailed guide to Seaborn &lt;a href="https://anvil.works/blog/plotting-in-seaborn?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s our election-results plot in Seaborn. You can see that the code is a lot simpler than the raw Matplotlib.&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fseaborn-tweaked.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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fseaborn-tweaked.png" alt="image"&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;seaborn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;long&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

    &lt;span class="c1"&gt;# Some boilerplate to initialise things
&lt;/span&gt;    &lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# This is where the actual plot gets made
&lt;/span&gt;    &lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;barplot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seats&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;party&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;palette&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;blue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;red&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yellow&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;grey&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;saturation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Customise some display properties
&lt;/span&gt;    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UK election results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#cccccc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Seats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_xlabel&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="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_xticklabels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vertical&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Ask Matplotlib to show it
&lt;/span&gt;    &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/blog/plotting-in-seaborn?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Full Guide &amp;amp; Example Code: Plotting in Seaborn &amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h1&gt;
  
  
  Plotly
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://anvil.works/blog/plotting-in-plotly?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Plotly&lt;/a&gt; is a plotting ecosystem that includes a Python plotting library. There are three different interfaces to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an object-oriented interface&lt;/li&gt;
&lt;li&gt;an imperative interface that allows you to specify your plot using JSON-like data structures&lt;/li&gt;
&lt;li&gt;and a high-level interface similar to Seaborn called Plotly Express.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plotly plots are designed to be embedded in web apps. At its core, Plotly is actually a JavaScript library! It uses D3 and stack.gl to draw the plots.&lt;/p&gt;

&lt;p&gt;You can build Plotly libraries in other languages by passing JSON to the JavaScript library. The official Python and R libraries do just that. At Anvil, we ported the Python Plotly API to &lt;a href="https://anvil.works/docs/client/components/plots?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;run in the web browser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the election results plot in Plotly:&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fplotting-in-anvil.gif" 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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fplotting-in-anvil.gif" alt="image"&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;plotly.graph_objects&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wide&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

    &lt;span class="c1"&gt;#  Get a convenient list of x-values
&lt;/span&gt;    &lt;span class="n"&gt;years&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;years&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="c1"&gt;# Specify the plots
&lt;/span&gt;    &lt;span class="n"&gt;bar_plots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#0343df&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#e50000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#ffff14&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#929591&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Customise some display properties
&lt;/span&gt;    &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Election results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;yaxis_title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Seats&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;xaxis_tickmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;array&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;xaxis_tickvals&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;xaxis_ticktext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Make the multi-bar plot
&lt;/span&gt;    &lt;span class="n"&gt;fig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bar_plots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Tell Plotly to render it
&lt;/span&gt;    &lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/blog/plotting-in-plotly?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Full Guide &amp;amp; Example Code: Plotting with Plotly &amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h1&gt;
  
  
  Bokeh
&lt;/h1&gt;

&lt;p&gt;Bokeh (pronounced “BOE-kay”) specialises in building interactive plots, so this example doesn’t show it off to its best. Check out &lt;a href="https://anvil.works/blog/plotting-in-bokeh?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;this extended guide to Bokeh&lt;/a&gt;, where we add some custom tooltips! Like Plotly, Bokeh’s plots are designed to be embedded in web apps - it outputs its plots as HTML files.&lt;/p&gt;

&lt;p&gt;Here are the election results plotted in Bokeh:&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fbokeh.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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fbokeh.png" alt="image"&gt;&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ColumnDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FactorRange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HoverTool&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.plotting&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;figure&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.transform&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;factor_cmap&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;long&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

    &lt;span class="c1"&gt;# Specify a file to write the plot to
&lt;/span&gt;    &lt;span class="nf"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;elections.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Tuples of groups (year, party)
&lt;/span&gt;    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;r&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;party&lt;/span&gt;&lt;span class="sh"&gt;'&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;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;seats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Bokeh wraps your data in its own objects to support interactivity
&lt;/span&gt;    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ColumnDataSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Create a colourmap
&lt;/span&gt;    &lt;span class="n"&gt;cmap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#0343df&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#e50000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#ffff14&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#929591&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fill_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factor_cmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;palette&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;factors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;start&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="n"&gt;end&lt;/span&gt;&lt;span class="o"&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;# Make the plot
&lt;/span&gt;    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_range&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;FactorRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Election results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vbar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fill_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fill_color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Customise some display properties
&lt;/span&gt;    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y_range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x_range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;range_padding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yaxis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;axis_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Seats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xaxis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;major_label_orientation&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xgrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grid_line_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/blog/plotting-in-bokeh?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Full Guide &amp;amp; Example Code: Plotting with Bokeh &amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h1&gt;
  
  
  Altair
&lt;/h1&gt;

&lt;p&gt;Altair is based on a declarative plotting language (or ‘visualisation grammar’) called &lt;a href="https://vega.github.io/vega/" rel="noopener noreferrer"&gt;Vega&lt;/a&gt;. This means a well-thought-through API that scales well for complex plots, saving you from getting lost in nested-for-loop hell.&lt;/p&gt;

&lt;p&gt;As with Bokeh, Altair outputs its plots as HTML files. Check out the extended guide to Altair &lt;a href="https://anvil.works/blog/plotting-in-altair?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how our election results plot looks in Altair. Note how expressively the actual plot is specified, with just six lines of Python:&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Faltair.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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Faltair.png" alt="image"&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;altair&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;long&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

    &lt;span class="c1"&gt;# Set up the colourmap
&lt;/span&gt;    &lt;span class="n"&gt;cmap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#0343df&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#e50000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#ffff14&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#929591&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Cast years to strings
&lt;/span&gt;    &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Here's where we make the plot
&lt;/span&gt;    &lt;span class="n"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mark_bar&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;X&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;party&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;seats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;party&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Save it as an HTML file.
&lt;/span&gt;    &lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;altair-elections.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/blog/plotting-in-altair?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Full Guide &amp;amp; Example Code: Plotting with Plotly &amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h1&gt;
  
  
  Pygal
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://anvil.works/blog/plotting-in-pygal?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Pygal&lt;/a&gt; focuses on visual appearance. It produces SVG plots by default, so you can zoom them forever – or print them out – without them getting pixellated. Pygal plots also come with some good interactivity features built-in, making Pygal another underrated candidate if you’re looking to embed plots in a web app.&lt;/p&gt;

&lt;p&gt;Here's the election results plot in Pygal:&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fpygal.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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fpygal.png" alt="image"&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pygal&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pygal.style&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Style&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wide&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

    &lt;span class="c1"&gt;# Define the style
&lt;/span&gt;    &lt;span class="n"&gt;custom_style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#0343df&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#e50000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#ffff14&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#929591&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;font_family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Roboto,Helvetica,Arial,sans-serif&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;transparent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;label_font_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Set up the bar plot, ready for data
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pygal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UK Election Results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;custom_style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;y_title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Seats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;x_label_rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;270&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Add four data sets to the bar plot
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conservative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;labour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;liberal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;others&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;# Define the X-labels
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x_labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Write this to an SVG file
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pygal.svg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/blog/plotting-in-pygal?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Full Guide &amp;amp; Example Code: Plotting with PyGal &amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h1&gt;
  
  
  Pandas
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://anvil.works/blog/plotting-in-pandas?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Pandas&lt;/a&gt; is an extremely popular Data Science library for Python. It allows you to do all sorts of data manipulation scalably, but it also has a convenient plotting API. Because it operates directly on data frames, our Pandas example is the most concise code snippet in this article – even shorter than the Seaborn one!&lt;/p&gt;

&lt;p&gt;The Pandas API is a wrapper around Matplotlib, so you can also use the underlying Matplotlib API to get fine-grained control of your plots.&lt;/p&gt;

&lt;p&gt;Here’s the plot in Pandas. The code is beautifully concise!&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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fpandas.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%2Fanvil.works%2Fblog%2Fimg%2Fplotting-in-python%2Fpandas.png" alt="image"&gt;&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;matplotlib.colors&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ListedColormap&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wide&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

    &lt;span class="n"&gt;cmap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ListedColormap&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#0343df&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#e50000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#ffff14&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#929591&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_xlabel&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="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Seats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UK election results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/blog/plotting-in-pandas?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Full Guide &amp;amp; Example Code: Plotting with Pandas &amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h1&gt;
  
  
  Plotting with Anvil
&lt;/h1&gt;

&lt;p&gt;Anvil is a system for building full-stack web apps with nothing but Python – no JavaScript required!&lt;/p&gt;

&lt;p&gt;You can use any of the libraries I’ve talked about in this article to make plots in Anvil, and display them in the web browser. It’s great for &lt;a href="https://anvil.works/learn/tutorials/dashboard?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;building dashboards&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdashboard%2Fbusiness-dash.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdashboard%2Fbusiness-dash.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In each of the in-depth guides to &lt;a href="https://anvil.works/blog/plotting-in-matplotlib?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Matplotlib&lt;/a&gt;, &lt;a href="https://anvil.works/blog/plotting-in-plotly?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Plotly&lt;/a&gt;, &lt;a href="https://anvil.works/blog/plotting-in-seaborn?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Seaborn&lt;/a&gt;, &lt;a href="https://anvil.works/blog/plotting-in-bokeh?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Bokeh&lt;/a&gt;, &lt;a href="https://anvil.works/blog/plotting-in-altair?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Altair&lt;/a&gt;, and &lt;a href="https://anvil.works/blog/plotting-in-pygal?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Pygal&lt;/a&gt;, you’ll find an example web application you can open and edit in Anvil, showing you how to use each of these Python plotting libraries.&lt;/p&gt;

&lt;p&gt;You can also find Anvil-specific advice in the &lt;a href="https://anvil.works/docs/how-to/plot?utm_source=crosspost:dev.to:/blog/plotting-in-python"&gt;Anvil plotting guide&lt;/a&gt;. It tells you how to use plots from each of the libraries mentioned in this article in your Anvil app, including the fully front-end Python version of Plotly. &lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Getting Photos from Mars with the NASA API</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Wed, 03 Mar 2021 11:58:08 +0000</pubDate>
      <link>https://dev.to/meredydd/getting-photos-from-mars-with-the-nasa-api-3l2l</link>
      <guid>https://dev.to/meredydd/getting-photos-from-mars-with-the-nasa-api-3l2l</guid>
      <description>&lt;h1&gt;
  
  
  Get the Latest Mars Photos, on the Web and In Your Inbox
&lt;/h1&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%2Fanvil.works%2Fblog%2Fimg%2Fphotos-from-mars%2Fperseverance.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%2Fanvil.works%2Fblog%2Fimg%2Fphotos-from-mars%2Fperseverance.jpg" alt="Perseverance rover"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everyone's waiting on the next photos from Perseverance, NASA's latest Mars rover. I'll show you how to use NASA's API to get the latest images as soon as they arrive - and send them straight to your inbox!&lt;/p&gt;

&lt;p&gt;We don't need our own &lt;a href="https://www.nasa.gov/directorates/heo/scan/services/networks/deep_space_network/about/" rel="noopener noreferrer"&gt;Deep Space Network&lt;/a&gt; -- NASA provides a &lt;a href="https://api.nasa.gov" rel="noopener noreferrer"&gt;public API&lt;/a&gt; for all of its Mars rovers. So I wrote an app to fetch the latest photos from Perseverance, and its older sibling Curiosity.&lt;/p&gt;

&lt;p&gt;I built the web app with &lt;a href="https://anvil.works?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;Anvil&lt;/a&gt;, which means I could build &lt;em&gt;everything&lt;/em&gt; in Python -- that includes the "front-end" parts (displaying images, choosing your rovers, entering your email address, and so on), as well as the "back-end" (accessing NASA's APIs and sending out emails).&lt;/p&gt;

&lt;p&gt;Take a look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://photos-from-mars.anvil.app" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanvil.works%2Fblog%2Fimg%2Fphotos-from-mars%2Fapp-screenshot.jpg" alt="App Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

See the latest photos from all the Mars rovers, and get an email as soon as new ones arrive.






&lt;h2&gt;
  
  
  Getting the Photos
&lt;/h2&gt;

&lt;p&gt;According to the &lt;a href="https://github.com/chrisccerami/mars-photo-api" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;, you can get all Perseverance's latest photos from this URL:&lt;br&gt;
&lt;br&gt;&lt;small&gt;&lt;a href="https://api.nasa.gov/mars-photos/api/v1/rovers/perseverance/latest_photos?api_key=DEMO_KEY" rel="noopener noreferrer"&gt;https://api.nasa.gov/mars-photos/api/v1/rovers/perseverance/latest_photos?api_key=DEMO_KEY&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The photos arrive as an array of JSON objects, describing everything about a photo. That includes which of the rover's cameras took it, the sol (and Earth date) of the photo, as well as a URL to download the image itself. Here's one:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;804712&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"earth_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;"2021-02-20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"camera"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MCZ_RIGHT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"full_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mast Camera Zoom - Right"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rover_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&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;"img_src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00002/ids/fdr/browse/zcam/ZRF_0002_0667131195_000FDR_N0010052AUT_04096_0260LUJ01_1200.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rover"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Perseverance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"landing_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;"2021-02-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"launch_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;"2020-07-30"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&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;h2&gt;
  
  
  Displaying the Photos
&lt;/h2&gt;

&lt;p&gt;The first thing to build is a web app that displays a random photo. I used Anvil's &lt;a href="https://anvil.works/articles/drag-and-drop-builder?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;drag and drop designer&lt;/a&gt; to set up an &lt;code&gt;Image&lt;/code&gt; component, and some &lt;code&gt;Label&lt;/code&gt;s to display information about the photo.&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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Farticles%2Fphotos-from-mars%2Fbuild-ui.gif" 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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Farticles%2Fphotos-from-mars%2Fbuild-ui.gif" alt="Building a simple UI with drag and drop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I wrote a &lt;a href="https://anvil.works/docs/server?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;server function&lt;/a&gt; to retrieve a photo. It started off really simple:&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="nd"&gt;@anvil.server.callable&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_photo&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.nasa.gov/mars-photos/api/v1/rovers/perseverance/latest_photos?api_key=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest_photos&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can call it from my Python code that's running in the web browser, and display the photo in our UI:&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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Farticles%2Fphotos-from-mars%2Fdisplay-image-shortened.gif%3Freally" 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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Farticles%2Fphotos-from-mars%2Fdisplay-image-shortened.gif%3Freally" alt="Writing code to display a photo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, the most recent image might not be the most interesting. What's more, there is more than one rover on Mars, and they send back images on their own schedules. So I expanded the app to look backwards through time until it can find at least 50 photos from each rover, then send them all to the front end, which shuffles them when the user clicks a button. (You can see how this works in the source code.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending Automatic Emails
&lt;/h2&gt;

&lt;p&gt;Of course, I could see all these photos on NASA's website if I wanted. I want to know when we get &lt;em&gt;new&lt;/em&gt; photos! Specifically, I want to get them by email. We'll need two new features for this: &lt;a href="https://anvil.works/docs/background-tasks/scheduled-tasks?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;Scheduled Tasks&lt;/a&gt;, to check for new photos, and the &lt;a href="https://anvil.works/docs/email?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;Email Service&lt;/a&gt;, to send them out. Plus, of course, a &lt;a href="https://anvil.works/docs/data-tables?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;database table&lt;/a&gt; to store the email addresses:&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%2Fanvil.works%2Fblog%2Fimg%2Fphotos-from-mars%2Fdata-table.png%3Futm_source%3Dcrosspost%3Adev.to%3A%2Fblog%2Fphotos-from-mars" 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%2Fanvil.works%2Fblog%2Fimg%2Fphotos-from-mars%2Fdata-table.png%3Futm_source%3Dcrosspost%3Adev.to%3A%2Fblog%2Fphotos-from-mars" alt="The Data Table storing email subscribers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every 10 minutes, the Scheduled Task collects the latest photos from every rover:&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="nd"&gt;@anvil.server.background_task&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_photos&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;latest_photos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.nasa.gov/mars-photos/api/v1/rovers/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rover&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/latest_photos?api_key=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest_photos&lt;/span&gt;&lt;span class="sh"&gt;"&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;rover&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ROVERS&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we look through every subscriber, and compare the date of our last email to them with the &lt;code&gt;earth_date&lt;/code&gt; of the most recent photos. If the photos are more recent, we pick one and send it as an email attachment, using &lt;a href="https://anvil.works/docs/email/attachments?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;&lt;code&gt;anvil.email.send()&lt;/code&gt;&lt;/a&gt;. The emails are grouped by day, so we will never send more than one email a day to any subscriber.&lt;/p&gt;

&lt;p&gt;The actual logic for this is a little cumbersome, but if you want to see it in more detail then you can check out the full source code:&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;You can open the app in the Anvil editor and see its source code here (requires a free Anvil account): &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/build#clone:T6VAXJF5RA2JEYIS=B4NKQJ45BLBBKI4DBNOGSMKM" rel="noopener noreferrer"&gt;Open Source Code &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;p&gt;Or you can check out the app again, now that you know how it works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://photos-from-mars.anvil.app" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanvil.works%2Fblog%2Fimg%2Fphotos-from-mars%2Fapp-screenshot.jpg" alt="App Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

See the latest photos from all the Mars rovers, and get an email as soon as new ones arrive.



&lt;p&gt;If you can think of something else cool to do with the Mars rover images, or NASA's APIs, please put it in the comments, or tweet it to me &lt;a href="https://twitter.com/meredydd" rel="noopener noreferrer"&gt;@meredydd&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  More about Anvil
&lt;/h2&gt;

&lt;p&gt;If you're new here, welcome! &lt;a href="https://anvil.works/?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;Anvil&lt;/a&gt; is a platform for building full-stack web apps with nothing but Python. No need to wrestle with JS, HTML, CSS, Python, SQL and all their frameworks – just &lt;strong&gt;build it all in Python&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://anvil.works/build?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;Try Anvil &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://anvil.works/learn?utm_source=crosspost:dev.to:/blog/photos-from-mars"&gt;See tutorials &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>python</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>12 Reasons to use Python, not JS, for Front End Web Development</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Tue, 16 Feb 2021 16:51:56 +0000</pubDate>
      <link>https://dev.to/meredydd/12-reasons-to-use-python-not-js-for-front-end-web-development-b4a</link>
      <guid>https://dev.to/meredydd/12-reasons-to-use-python-not-js-for-front-end-web-development-b4a</guid>
      <description>&lt;h1&gt;
  
  
  Why Python Beats HTML+JS for Web Development
&lt;/h1&gt;

&lt;p&gt;Web development is pretty unwieldy. You need to master JS &lt;em&gt;and&lt;/em&gt; HTML &lt;em&gt;and&lt;/em&gt; CSS &lt;em&gt;and&lt;/em&gt; Python (or Rails or Node) &lt;em&gt;and&lt;/em&gt; a &lt;a href="https://github.com/kamranahmedse/developer-roadmap#frontend-roadmap" rel="noopener noreferrer"&gt;ton of frameworks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We love Python because of its motto: &lt;a href="https://zen-of-python.info/" rel="noopener noreferrer"&gt;Simple is better than complex&lt;/a&gt;. &lt;strong&gt;So what would web development look like, if it were 100% Python -- even the front end?&lt;/strong&gt; We built it, and it's called &lt;a href="https://anvil.works/?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Anvil&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are 12 reasons why building your front-end &lt;em&gt;and&lt;/em&gt; back-end in Python is so great:&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Your UI components are all Python objects.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://anvil.works/python-browser?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Running Python in the browser&lt;/a&gt; means you can modify your UI components in Python. Drag and drop them onto the page to build the user interface, then set their properties and call events on them from Python code. &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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Ffeedback-form%2Fadd-button.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Ffeedback-form%2Fadd-button.gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Building the UI for a feedback form. Check out the &lt;a href="https://anvil.works/learn/tutorials/feedback-form?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;tutorial&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Call server-side functions from the browser
&lt;/h3&gt;

&lt;p&gt;In traditional web-dev, calling from the browser to the web server is a pain. You have to set up a URL route, smush all your data into JSON, set up the AJAX request, asynchronously get a response...so much work!&lt;/p&gt;

&lt;p&gt;With Anvil, you &lt;strong&gt;just call a function.&lt;/strong&gt; &lt;a href="https://anvil.works/docs/server/quickstart?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Add a decorator&lt;/a&gt; to any function, then just call the function from browser code. Pass Python objects as arguments; return Python objects. Job done.&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%2Fanvil.works%2Fblog%2Fimg%2F11-reasons-python-front-end-web-development%2Fdrake.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%2Fanvil.works%2Fblog%2Fimg%2F11-reasons-python-front-end-web-development%2Fdrake.jpg"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. The database is built-in
&lt;/h3&gt;

&lt;p&gt;Setting up and maintaining a database is a drag. So Anvil has a built-in database. Design your &lt;a href="https://anvil.works/docs/data-tables?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;data tables&lt;/a&gt; graphically, then query or update rows with Python. (Can you return a lazy paginated query response to the browser, as a Python object? Of course you can! That would be &lt;em&gt;dozens&lt;/em&gt; of lines of code in most web frameworks.)&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%2Fanvil.works%2Fblog%2Fimg%2Fdashboard%2Fdata-table.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%2Fanvil.works%2Fblog%2Fimg%2Fdashboard%2Fdata-table.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A Data Table in Anvil&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Connect your code running &lt;em&gt;anywhere&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Anvil is "serverless" - your code is automatically hosted in the cloud. But what if you want to run code on your computer? Just use the &lt;a href="https://anvil.works/docs/uplink?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Uplink&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Got a Jupyter notebook?&lt;/strong&gt; &lt;a href="https://anvil.works/learn/tutorials/jupyter-notebook-to-web-app?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Call it from the web&lt;/a&gt;!&lt;br&gt;&lt;strong&gt;Got a local database?&lt;/strong&gt; &lt;a href="https://anvil.works/learn/tutorials/external-database?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Write a local script&lt;/a&gt; to query it, then call it from the web!&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fgoogle-colab-notebook-to-web-app%2Fgoogle_colab_demo.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fgoogle-colab-notebook-to-web-app%2Fgoogle_colab_demo.gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Connecting a &lt;a href="https://anvil.works/learn/tutorials/google-colab-to-web-app?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Google Colab notebook to a web app&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Binary data is easier to handle
&lt;/h3&gt;

&lt;p&gt;"Uploading a file" is basic functionality. So it must be simple in every web framework. Right? &lt;strong&gt;Surprise!&lt;/strong&gt; Handling binary data -- like files, images, or PDFs -- is remarkably difficult in a traditional JS app. (If you're feeling mean, try saying '&lt;a href="https://stackoverflow.com/questions/4526273/what-does-enctype-multipart-form-data-mean" rel="noopener noreferrer"&gt;&lt;code&gt;enctype="multipart/form-data"&lt;/code&gt;&lt;/a&gt;' to a seasoned web developer. Watch them shiver.)&lt;/p&gt;

&lt;p&gt;But Anvil makes it easy. All binary data (pictures, uploaded files, etc.) is represented as &lt;a href="https://anvil.works/docs/media?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;a Python object&lt;/a&gt;! You can pass binary data as an argument to a server function. You can store it in a Data Table. You can use it with Anvil components. For example, &lt;a href="https://anvil.works/learn/tutorials/pdfs?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;rendering and downloading a PDF&lt;/a&gt; is literally this simple:&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;# In a server module:
&lt;/span&gt;&lt;span class="nd"&gt;@anvil.server.callable&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_pdf&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Form1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In the browser:
&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_pdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;








&lt;h3&gt;
  
  
  6. User authentication comes built-in
&lt;/h3&gt;

&lt;p&gt;Building user authentication is tedious, but &lt;em&gt;deadly&lt;/em&gt; if you get it wrong! Half of the &lt;a href="https://owasp.org" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt; vulnerabilities are "ways you can get authentication wrong".&lt;/p&gt;

&lt;p&gt;Anvil's built in &lt;a href="https://anvil.works/docs/users?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;User Service&lt;/a&gt; handles signup, login, and user permissions for you, out of the box. It takes one line of Python code to present your users with a signup form with email validation -- just call &lt;code&gt;anvil.users.login_with_form()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As well as email login, Anvil supports &lt;a href="https://anvil.works/docs/integrations/google?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Google&lt;/a&gt;, &lt;a href="https://anvil.works/docs/integrations/microsoft?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Microsoft&lt;/a&gt;, &lt;a href="https://anvil.works/docs/integrations/facebook?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Facebook&lt;/a&gt;, and &lt;a href="https://anvil.works/docs/integrations/saml?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;SAML SSO&lt;/a&gt;. (What about two-factor authentication? &lt;a href="https://anvil.works/docs/users/two_factor_authentication?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Of course it works.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fmulti-user-apps%2Fthumbnail.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fmulti-user-apps%2Fthumbnail.png"&gt;&lt;/a&gt;&lt;/p&gt;






&lt;h3&gt;
  
  
  7. Email support is built-in too
&lt;/h3&gt;

&lt;p&gt;Send an email with one line of code. &lt;em&gt;Receive&lt;/em&gt; emails with one line of code! It's all built in with Anvil's &lt;a href="https://anvil.works/docs/email?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Email service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Building an app to receive email is so simple, we did it in a &lt;strong&gt;4-minute video&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GoLRmSKWySU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;






&lt;h3&gt;
  
  
  8. PDF generation is &lt;em&gt;also&lt;/em&gt; built-in
&lt;/h3&gt;

&lt;p&gt;Did someone say "batteries included"? Create PDF documents with our drag-n-drop editor, then render and download them with a Python call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://anvil.works/learn/tutorials/pdfs?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-thumbnail.png"&gt;&lt;/a&gt;&lt;/p&gt;






&lt;h3&gt;
  
  
  9. Use your favorite Python packages
&lt;/h3&gt;

&lt;p&gt;One of the best features of Python is the numerous packages available. With the &lt;a href="https://anvil.works/docs/server/packages?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Full Python runtime&lt;/a&gt;, you have access to a &lt;a href="https://anvil.works/docs/server/packages?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;long list&lt;/a&gt; of your favorite Python libraries to build your web apps.&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%2Fmedia2.giphy.com%2Fmedia%2Fj2wpZyLy2s70ul4TKo%2Fgiphy.gif%3Fcid%3Decf05e47az6uq49bv7hh2c2a78dzih2ed57fkmyelbbtc9lg%26rid%3Dgiphy.gif" 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%2Fmedia2.giphy.com%2Fmedia%2Fj2wpZyLy2s70ul4TKo%2Fgiphy.gif%3Fcid%3Decf05e47az6uq49bv7hh2c2a78dzih2ed57fkmyelbbtc9lg%26rid%3Dgiphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;






&lt;h3&gt;
  
  
  10. You can still build HTTP APIs
&lt;/h3&gt;

&lt;p&gt;Want to build an HTTP API, so non-Anvil apps can interface with your service? &lt;a href="https://anvil.works/docs/http-apis/creating-http-endpoints?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Creating HTTP endpoints&lt;/a&gt; is &lt;em&gt;nearly&lt;/em&gt; as simple as making functions you can call from the browser. Check out &lt;a href="https://anvil.works/learn/tutorials/http-apis-in-python?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;this tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Or just watch Bridget build and deploy a JSON API in 20 seconds:&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%2Fanvil.works%2Fimg%2Ffeatures%2Fhttp-demo.gif" 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%2Fanvil.works%2Fimg%2Ffeatures%2Fhttp-demo.gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Creating and deploying a working JSON API in 20 seconds.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  11. One-click integrations
&lt;/h3&gt;

&lt;p&gt;Your Anvil app can &lt;a href="https://anvil.works/docs/integrations?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;easily connect&lt;/a&gt; to services from Google, Microsoft, Facebook, Stripe and more. Log your users in using Google, Microsoft and Facebook Single Sign-Ons, &lt;a href="https://anvil.works/docs/integrations/stripe?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;take payments&lt;/a&gt; with Stripe, or display interactive &lt;a href="https://anvil.works/docs/client/components/maps?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Google Maps&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%2Fanvil.works%2Fimg%2Ffeatures%2Fmap3.gif" 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%2Fanvil.works%2Fimg%2Ffeatures%2Fmap3.gif" alt="Google Maps, integrated into a Python web app"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  12. Easy encrypted storage
&lt;/h3&gt;

&lt;p&gt;You don't want to leave passwords lying around in your source code. The &lt;a href="https://anvil.works/docs/security/encrypting-secret-data?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;App Secrets&lt;/a&gt; service provides easy-to-use encrypted storage of sensitive data, like passwords or encryption keys.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn to store encrypted data with another &lt;a href="https://anvil.works/learn/tutorials/app-secrets?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;4-minute tutorial&lt;/a&gt;:&lt;/em&gt;&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UEdLxZDglY0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h3&gt;
  
  
  Get Simple. Keep the Power.
&lt;/h3&gt;

&lt;p&gt;Anvil gives you all the power of Python, and none of the complexity of traditional web frameworks. If you're a Python developer, you can build full-stack web apps without needing anything else.&lt;/p&gt;

&lt;p&gt;And if you &lt;em&gt;do&lt;/em&gt; want to tweak something with HTML, CSS or JavaScript, there's always an &lt;a href="https://anvil.works/blog/escape-hatches-and-ejector-seats?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;escape hatch&lt;/a&gt;! Use a &lt;a href="https://anvil.works/docs/client/javascript?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Javascript library&lt;/a&gt; from Python code, or &lt;a href="https://anvil.works/docs/client/themes-and-styling?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;style your app with HTML CSS&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't get locked in.
&lt;/h3&gt;

&lt;p&gt;Anvil's runtime is &lt;a href="https://anvil.works/open-source?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;open source&lt;/a&gt;, so you can take your app and deploy it anywhere. You don't even need our editor to create an Anvil app!&lt;/p&gt;




&lt;h2&gt;
  
  
  Start Building
&lt;/h2&gt;

&lt;p&gt;Anvil is free is to use, so you can start building right away! Start with &lt;a href="https://anvil.works/learn/tutorials?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;a tutorial&lt;/a&gt; to get acquainted with Anvil or check out the &lt;a href="https://anvil.works/docs/overview?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;documentation&lt;/a&gt; to learn about some of Anvil's other features. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://anvil.works/build?utm_source=crosspost:dev.to:/blog/reasons-python-front-end-web-development"&gt;Try Anvil &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cracker Jokes as a Service with Python</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Wed, 23 Dec 2020 15:56:22 +0000</pubDate>
      <link>https://dev.to/meredydd/cracker-jokes-as-a-service-with-python-2hna</link>
      <guid>https://dev.to/meredydd/cracker-jokes-as-a-service-with-python-2hna</guid>
      <description>&lt;p&gt;Christmas crackers from the local supermarket often have the worst jokes. We say, 'there must be a better way'. Save up your favourite jokes this Christmas and hear the sound of guffaws 🤣 , not the silence of cringing faces 😬 .&lt;/p&gt;

&lt;p&gt;It's database-backed, it's on the Web, it's in Python...and we didn't have to write a line of Javascript -- yep, we used &lt;a href="https://anvil.works" rel="noopener noreferrer"&gt;Anvil&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%2Fanvil.works%2Fadvent%2Fimg%2Fcracker-jokes%2Fjokes.gif" 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%2Fanvil.works%2Fadvent%2Fimg%2Fcracker-jokes%2Fjokes.gif" alt="The sample data is the most interesting part, really"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Canvas Component
&lt;/h2&gt;

&lt;p&gt;The anvil &lt;a href="https://anvil.works/docs/client/components/canvas" rel="noopener noreferrer"&gt;Canvas&lt;/a&gt; component gives us access to the browser's drawing APIs, along with easy mouse events -- so that seemed like the perfect way to build this app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw_background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;URLMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_/theme/cracker.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;margin&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_width&lt;/span&gt;&lt;span class="p"&gt;()&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;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_height&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;canvas_mouse_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;event_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;This method is called when a mouse button is pressed on this component&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idx&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="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jokes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idx&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="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jokes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_joke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Batteries Included
&lt;/h2&gt;

&lt;p&gt;Anvil's &lt;a href="https://anvil.works/blog/python-in-the-browser" rel="noopener noreferrer"&gt;Python in the browser&lt;/a&gt; comes with Python's &lt;a href="https://www.python.org/dev/peps/pep-0206/" rel="noopener noreferrer"&gt;batteries included&lt;/a&gt; philosophy, from your favourite python libraries to f-strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TellJokes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TellJokesTemplate&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jokes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crackers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idx&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QorA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;QA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# switch between Q and A
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;laughing&lt;/span&gt; &lt;span class="o"&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Click To Start Telling Jokes 🎅&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_joke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;QorA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QorA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;QorA&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jokes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;QorA&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;QorA&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;laughing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# add a random laugh emoji
&lt;/span&gt;
        &lt;span class="c1"&gt;# wrap lines for readability
&lt;/span&gt;        &lt;span class="n"&gt;wrapped_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_width&lt;/span&gt;&lt;span class="p"&gt;()&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Share the laughter
&lt;/h2&gt;

&lt;p&gt;I used the Anvil &lt;a href="https://anvil.works/docs/data-tables" rel="noopener noreferrer"&gt;Data Tables&lt;/a&gt; service to create a database, then built a UI with a &lt;a href="https://anvil.works/docs/client/components/data-grids" rel="noopener noreferrer"&gt;DataGrid&lt;/a&gt; component. Now I can send the app to the family -- let's hope they come up with some better jokes than I did 🙄 .&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%2Fanvil.works%2Fadvent%2Fimg%2Fcracker-jokes%2Fadd_joke.gif" 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%2Fanvil.works%2Fadvent%2Fimg%2Fcracker-jokes%2Fadd_joke.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  View the source code
&lt;/h2&gt;

&lt;p&gt;Want to play with the app yourself? Check out the source code:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://anvil.works/build#clone:77LQJTEIQFXI52UP=MJSVCAHRUNQNUIKKYGFFUF44" rel="noopener noreferrer"&gt;Open source code &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h4&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>christmas</category>
    </item>
    <item>
      <title>Drawing 3D Models in the Browser - with Python!</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Fri, 18 Dec 2020 10:18:32 +0000</pubDate>
      <link>https://dev.to/meredydd/drawing-3d-models-with-python-in-the-browser-21</link>
      <guid>https://dev.to/meredydd/drawing-3d-models-with-python-in-the-browser-21</guid>
      <description>&lt;h2&gt;
  
  
  A 3D Christmas Tree in the Browser - With Nothing But Python
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Good news:&lt;/strong&gt; It's time to decorate the tree!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad news:&lt;/strong&gt; Nobody will see it inside my house this year. &lt;/p&gt;

&lt;p&gt;So I'm putting up a virtual one, in 3D.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Even more good news:&lt;/strong&gt; I can do it all with nothing but Python!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://anvil.works" rel="noopener noreferrer"&gt;Anvil&lt;/a&gt; lets me build web apps with Python. And I can use &lt;a href="https://threejs.org" rel="noopener noreferrer"&gt;three.js&lt;/a&gt;, the browser-based 3D engine - without writing any Javascript at all! That's because Anvil lets you &lt;a href="https://anvil.works/docs/client/javascript" rel="noopener noreferrer"&gt;use Javascript libraries from Python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Here's what the finished app looks like:&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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Fadvent-calendar%2F3d-tree%2FSelection_418.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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Fadvent-calendar%2F3d-tree%2FSelection_418.png" alt="The finished app"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;a href="https://3d-tree.anvil.app" rel="noopener noreferrer"&gt;Try it yourself &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;br&gt;If you're feeling impatient, you can just &lt;a href="https://anvil.works/build#clone:K6XYLHB3Z6VZDLDQ=UNU6F7PYDLXY4JC34KB6YEPV" rel="noopener noreferrer"&gt;grab the source code&lt;/a&gt; - or read on to find out how it works.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I built it
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Setup
&lt;/h3&gt;

&lt;p&gt;Driving a Javascript library in Anvil is easy. First, I included the library in my &lt;a href="https://anvil.works/docs/client/javascript#using-native-javascript-libraries" rel="noopener noreferrer"&gt;Native Libraries&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/three.js/r122/three.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can open my Python code and import the &lt;code&gt;three.js&lt;/code&gt; library into Python, with &lt;code&gt;from anvil.js.window import THREE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'm making my tree out of three &lt;code&gt;ConeGeometry&lt;/code&gt; objects -- although they're Javascript objects, we can construct them straight from Python. Here's the Python code that actually builds the tree:&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;# Import the three.js library from Javascript-land:
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;anvil.js.window&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;THREE&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;geom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ConeGeometry&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;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;cone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;geom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;green_tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;cone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
  &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila: A tree -- in my browser, built with Python:&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%2Fanvil.works%2Fadvent%2Fimg%2F3d-tree%2Fsimple-tree.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%2Fanvil.works%2Fadvent%2Fimg%2F3d-tree%2Fsimple-tree.png" alt="A 3D Christmas tree with no decoration"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Time to decorate!
&lt;/h3&gt;

&lt;p&gt;Now, a tree on its own isn't very interesting, so it's time to add some baubles. I'm adding spheres of random colours along the outside of each cone.&lt;/p&gt;

&lt;p&gt;So, for each cone, we do some trigonometry - we'll walk round the cone, adding baubles at random intervals. Remember that a cone is wider at the bottom than at the top, so we use Python's &lt;code&gt;random.triangular()&lt;/code&gt; to pick lower positions more often:&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;bauble_geom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SphereGeometry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;theta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;theta&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

          &lt;span class="c1"&gt;# Move a bit further round the tree
&lt;/span&gt;          &lt;span class="n"&gt;theta&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;triangular&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="mf"&gt;0.5&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="c1"&gt;# How far down the cone? (0=top point, 1=bottom edge)
&lt;/span&gt;          &lt;span class="n"&gt;fh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;triangular&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="c1"&gt;# What's the radius of the cone, this far down?
&lt;/span&gt;          &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;

          &lt;span class="c1"&gt;# What colour?
&lt;/span&gt;          &lt;span class="n"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MeshLambertMaterial&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;color&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hsl(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, 100%, 50%)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="n"&gt;bauble&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bauble_geom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;material&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

          &lt;span class="c1"&gt;# Hang it on the tree
&lt;/span&gt;          &lt;span class="n"&gt;bauble&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;cone_base&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="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="n"&gt;cone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bauble&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Making it interactive
&lt;/h3&gt;

&lt;p&gt;A Christmas tree is a personal expression, and everyone wants to customise theirs. So I've used Anvil's &lt;a href="https://anvil.works/articles/drag-and-drop-builder" rel="noopener noreferrer"&gt;drag-and-drop UI designer&lt;/a&gt; to build a little control panel, and wire up the dropdowns to control the construction of the tree:&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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Fadvent-calendar%2F3d-tree%2Fadd-tree-control.gif" 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%2Fanvil-website-static.s3.eu-west-2.amazonaws.com%2Fadvent-calendar%2F3d-tree%2Fadd-tree-control.gif" alt="Adding UI to control the tree"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Sharing it with the world
&lt;/h3&gt;

&lt;p&gt;Time to publish my app and share it with the world! You can open it here:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://3d-tree.anvil.app" rel="noopener noreferrer"&gt;https://3d-tree.anvil.app&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;And you can get the whole source code here:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://anvil.works/build#clone:K6XYLHB3Z6VZDLDQ=UNU6F7PYDLXY4JC34KB6YEPV" rel="noopener noreferrer"&gt;Source code &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Happy decorating! And if you enjoyed this, you should check out the rest of the &lt;a href="https://anvil.works/advent" rel="noopener noreferrer"&gt;Anvil Advent Calendar&lt;/a&gt;. We're building a web app a day with nothing but Python -- every day until Christmas!&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>graphics</category>
    </item>
    <item>
      <title>Python objects speak for themselves</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Wed, 02 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/meredydd/python-objects-speak-for-themselves-2ii8</link>
      <guid>https://dev.to/meredydd/python-objects-speak-for-themselves-2ii8</guid>
      <description>&lt;p&gt;&lt;em&gt;(Hat-tip to my former colleague Shaun for this one!)&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Introspection in Python
&lt;/h1&gt;

&lt;p&gt;Let’s investigate how you can use built-in Python tools to both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;find out exactly what any module can do, and&lt;/li&gt;
&lt;li&gt;find out exactly how Python will execute your code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes debugging Python code much easier than in other languages I’ve used. You can ask any object what it does and what data it holds - and this is &lt;em&gt;built in to the language!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’s a very basic Python class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A really simple class to represent vehicles.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;red&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wheels&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colour&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;repaint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Change the colour of this vehicle.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colour&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I import this class and run &lt;code&gt;dir&lt;/code&gt; on it, I can see everything it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Let's see what's in my vehicle...
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __class__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __delattr__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __dict__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __dir__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __doc__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __eq__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __format__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __ge__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __getattribute__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __gt__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __hash__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __init__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __init_subclass__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __le__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __lt__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __module__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __ne__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __new__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __reduce__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __reduce_ex__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __repr__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __setattr__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __sizeof__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __str__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __subclasshook__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __weakref__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;colour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;repaint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wheels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can see the method and attributes I gave the class: &lt;code&gt;repaint&lt;/code&gt;, &lt;code&gt;colour&lt;/code&gt; and &lt;code&gt;wheels&lt;/code&gt;. Not only that, but &lt;code&gt;dir&lt;/code&gt; also tells me about all the built-in methods that my class has inherited from Python’s &lt;code&gt;object&lt;/code&gt; class, such as &lt;code&gt;__eq__&lt;/code&gt; and&lt;code&gt;__str__&lt;/code&gt;. These all start and end with a &lt;strong&gt;d&lt;/strong&gt; ouble &lt;strong&gt;under&lt;/strong&gt; score so we refer to them as ‘dunder’ methods. Let’s try one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# I wonder what this method does...
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;__dict__&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wheels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;colour&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;red&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So my simple class automatically has a method to get its attributes in a dictionary &lt;sup id="fnr-footnotes-dict"&gt;1&lt;/sup&gt;! I didn’t know that… but I found it out just by introspecting my object using &lt;code&gt;dir&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forensic programming
&lt;/h2&gt;

&lt;p&gt;If something isn’t very well documented in a library you’re using, don’t worry! You can inspect the objects the library provides and work things out for yourself.&lt;/p&gt;

&lt;p&gt;Imagine you want to make an HTTP GET request. You’ve been recommended &lt;a href="https://requests.readthedocs.io/en/master/" rel="noopener noreferrer"&gt;the &lt;code&gt;requests&lt;/code&gt; library&lt;/a&gt;but let’s pretend you can’t make head or tail of the documentation &lt;sup id="fnr-footnotes-requests"&gt;2&lt;/sup&gt;. So, you try importing it and running &lt;code&gt;dir&lt;/code&gt; on it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# So, what does this library give me?
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ConnectTimeout&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ConnectionError&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DependencyWarning&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FileModeWarning&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HTTPError&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;NullHandler&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PreparedRequest&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReadTimeout&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RequestException&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RequestsDependencyWarning&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Session&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Timeout&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TooManyRedirects&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;URLRequired&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __author__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __author_email__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __build__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __builtins__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __cached__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __cake__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __copyright__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __description__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __doc__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __file__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __license__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __loader__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __name__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __package__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __path__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __spec__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __title__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __url__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; __version__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_check_cryptography&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_internal_utils&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;adapters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auth&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;certs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chardet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;check_compatibility&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;codes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;compat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cookies&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exceptions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;head&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hooks&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logging&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;options&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;packages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;patch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;put&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sessions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status_codes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;structures&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;urllib3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utils&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;warnings&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Quite a lot!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s a lot there! Looks like there’s a method named &lt;code&gt;get&lt;/code&gt;, maybe that will make a GET request for us? Let’s see if it’s callable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! But &lt;em&gt;how&lt;/em&gt; do we call it? We’ll use a brilliant built-in module named &lt;code&gt;inspect&lt;/code&gt; to tell us. It can tell us the signature of any function or method we might be thinking of calling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# What parameters does requests.get accept?
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Signature &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="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="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s a positional argument called &lt;code&gt;url&lt;/code&gt;, and an optional keyword argument named &lt;code&gt;params&lt;/code&gt;. The &lt;code&gt;url&lt;/code&gt; argument is obvious. Let’s try making a request to an API that returns Bertrand Russell quotes (what else?):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://bertrand.anvil.app/_/api/quote&lt;/span&gt;&lt;span class="sh"&gt;"&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;Response&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok… so I’ve got a response. I guess &lt;code&gt;200&lt;/code&gt; is an HTTP 200 OK status, meaning it worked. But how do I get my quote? One more use of &lt;code&gt;dir&lt;/code&gt; and I see that my response has a &lt;code&gt;text&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://bertrand.anvil.app/_/api/quote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Everything is vague to a degree you do not realize till you have tried to make it precise.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazing! We’ve worked out how to use the Python &lt;code&gt;requests&lt;/code&gt; library using nothing but our wits and Python’s built-in introspection tools.&lt;/p&gt;

&lt;p&gt;Incidentally, I spotted something strange when I ran &lt;code&gt;dir(requests)&lt;/code&gt;. What is &lt;code&gt;requests. __cake__&lt;/code&gt;? Maybe it ‘bakes’ a response into something I can store on disk? Maybe it’s got something to do with browser cookies? Let’s find out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# What's this?
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;__cake__&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# I see.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we’ve seen how Python’s introspection can help you figure out &lt;em&gt;precisely&lt;/em&gt; how to use a library, even when that library contains undocumented methods.&lt;/p&gt;

&lt;p&gt;Let’s get even more introspective. Python can gaze into its innards and see all the way through to its very guts. It can tell you all the details of how it compiles and executes your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Extreme&lt;/strong&gt; introspection
&lt;/h2&gt;

&lt;p&gt;Not only will Python objects tell you what they do, but Python can tell you exactly how it plans to do it. When Python executes your code, it does it in (at least) three steps, and you can find out the exact details of each of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Python understands code
&lt;/h3&gt;

&lt;p&gt;The first step is &lt;strong&gt;parsing&lt;/strong&gt;. Python takes the raw characters of your code and turns it into a structure that represents the meaning of the text. The long string of characters is translated into &lt;strong&gt;tokens&lt;/strong&gt; that are organised into a tree - an &lt;strong&gt;abstract syntax tree&lt;/strong&gt; (AST). Take a simple Python statement:&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The abstract syntax tree looks like this:&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%2Fanvil.works%2Fblog%2Fimg%2Fintrospection-in-python%2Fast-diagram-abstract.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%2Fanvil.works%2Fblog%2Fimg%2Fintrospection-in-python%2Fast-diagram-abstract.png" alt="What the Python parser sees when you tell it x = 4 + 8"&gt;&lt;/a&gt;&lt;br&gt;
                &lt;p&gt;What the Python parser sees when you tell it &lt;code&gt;x = 4 + 8&lt;/code&gt;&lt;/p&gt;
&lt;br&gt;
             &lt;/p&gt;

&lt;p&gt;This is something that must occur in all programming languages except assembly code, but not all give you such easy access to their innards as Python does. Python ships with a module you can run on your code to see the abstract syntax tree it generates from your code. It’s called &lt;code&gt;ast&lt;/code&gt; (&lt;a href="https://docs.python.org/3/library/ast.html" rel="noopener noreferrer"&gt;here is it in the Python docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We can get the AST for our simple statement like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import ast
&amp;gt;&amp;gt;&amp;gt; tree = ast.parse('x = 4 + 8')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s use our introspection skills to explore that object. We run &lt;code&gt;dir&lt;/code&gt; on it and see that there’s a &lt;code&gt;body&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; tree.body
[&amp;lt;_ast.Assign object at 0x1024a8470&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The body of the AST is a list of all the Python statements in the code. Our program only has one Python statement, the assignment statement &lt;code&gt;x = 4 + 8&lt;/code&gt;. That’s why &lt;code&gt;tree&lt;/code&gt; contains one statement, named &lt;code&gt;Assign&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s see what’s inside the assignment. Using &lt;code&gt;dir&lt;/code&gt; tells us it has things called &lt;code&gt;targets&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;. I bet &lt;code&gt;targets&lt;/code&gt; are the things being assigned to (&lt;code&gt;x&lt;/code&gt; in our case) and &lt;code&gt;value&lt;/code&gt; are the things being assigned (so for us that’s &lt;code&gt;4 + 8&lt;/code&gt;). We’ll check &lt;code&gt;targets&lt;/code&gt; first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; tree.body[0].targets[0].id
'x'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just as we expected. Now let’s look at &lt;code&gt;value&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;&amp;gt;&amp;gt; tree.body[0].value
&amp;lt;_ast.BinOp object at 0x1014e3128&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a &lt;code&gt;BinOp&lt;/code&gt; object - a binary operator, meaning an operator that takes two arguments. That must be our &lt;code&gt;+&lt;/code&gt; operator! We run &lt;code&gt;dir&lt;/code&gt; again to see what it does, and it looks like we want to see the &lt;code&gt;op&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; tree.body[0].value.op
&amp;lt;_ast.Add object at 0x1014d3978&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yep, just as we expected - &lt;code&gt;Add&lt;/code&gt; is clearly the token representing the &lt;code&gt;+&lt;/code&gt; operator. Where are the &lt;code&gt;4&lt;/code&gt; and &lt;code&gt;8&lt;/code&gt;? They’re in the &lt;code&gt;BinOp&lt;/code&gt;, which has &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;right&lt;/code&gt; attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; tree.body[0].value.left.n
4
&amp;gt;&amp;gt;&amp;gt; tree.body[0].value.right.n
8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we have the entire AST we sketched out before, represented by Python objects in a nested structure that reflects the structure of the tree!&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%2Fanvil.works%2Fblog%2Fimg%2Fintrospection-in-python%2Fast-diagram-code.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%2Fanvil.works%2Fblog%2Fimg%2Fintrospection-in-python%2Fast-diagram-code.png" alt="How ast expresses x = 4 + 8"&gt;&lt;/a&gt;&lt;br&gt;
                &lt;p&gt;How &lt;code&gt;ast&lt;/code&gt; expresses &lt;code&gt;x = 4 + 8&lt;/code&gt;&lt;/p&gt;
&lt;br&gt;
             &lt;/p&gt;
&lt;h3&gt;
  
  
  How Python executes code
&lt;/h3&gt;

&lt;p&gt;So Python turns the characters that make up our code into an AST representing the &lt;em&gt;meaning&lt;/em&gt; of the code, and it gives us the &lt;code&gt;ast&lt;/code&gt; module to inspect that for ourselves. But there’s more to executing the code than this. The standard Python implementation (&lt;a href="https://github.com/python/cpython" rel="noopener noreferrer"&gt;CPython&lt;/a&gt;) compiles your lovingly hand-crafted Python code into a form of assembly language - Python Bytecode - that runs on a virtual machine. This is how Python can ‘write once, run anywhere’. The virtual machine is compiled for various different types of computer, and the bytecode that runs on it is completely universal.&lt;/p&gt;

&lt;p&gt;Just as with &lt;code&gt;ast&lt;/code&gt;, Python has a standard library module called &lt;code&gt;dis&lt;/code&gt; that can show you the bytecode generated from a given Python program. You can pass in a Python object such as a function, or you can pass in a Python program as a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from dis import dis
&amp;gt;&amp;gt;&amp;gt; dis('x = 4 + 8')
  1 0 LOAD_CONST 0 (12)
              2 STORE_NAME 0 (x)
              4 LOAD_CONST 1 (None)
              6 RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this is an extremely simple Python program, it’s not hard to understand the bytecode. The first line loads a constant onto the Python virtual machine’s memory stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  1 0 LOAD_CONST 0 (12)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of that constant is included in the actual bytecode, and there’s a little optimisation here. The addition has already been carried out! The compiler has converted the &lt;code&gt;4 + 8&lt;/code&gt; into &lt;code&gt;12&lt;/code&gt; so the VM doesn’t need to do that at runtime.&lt;/p&gt;

&lt;p&gt;The next instruction is our assignment operator (the &lt;code&gt;=&lt;/code&gt;). It instructs the VM to define a symbol &lt;code&gt;x&lt;/code&gt; and store the value at position &lt;code&gt;0&lt;/code&gt; in it. This means it can refer to &lt;code&gt;x&lt;/code&gt; later on in the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              2 STORE_NAME 0 (x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler appears to be a little stupid here. Doesn’t it realise that this is the end of the code, and there is no ‘later on’? In fact, our tiny program &lt;code&gt;x = 4 + 8&lt;/code&gt; is a Python &lt;em&gt;module&lt;/em&gt;, so the bytecode keeps track of the label &lt;code&gt;x&lt;/code&gt; in case any other modules want to import it.&lt;/p&gt;

&lt;p&gt;There are two more instructions that we might not have expected to see. First, &lt;code&gt;None&lt;/code&gt; is loaded onto the stack. Next, the &lt;code&gt;RETURN_VALUE&lt;/code&gt; instruction is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              4 LOAD_CONST 1 (None)
              6 RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We didn’t tell it to return &lt;code&gt;None&lt;/code&gt;!? But all Python functions and modules do this by default, so these phantom instructions have appeared in order to enforce that useful consistency.&lt;/p&gt;

&lt;h3&gt;
  
  
  The final stage
&lt;/h3&gt;

&lt;p&gt;I said there were (at least) three stages to running a Python program. We’ve discussed the first two, and Python gives you absolutely precise introspection tools to pick them apart exactly.&lt;/p&gt;

&lt;p&gt;If you inspect the ASTs and bytecode your hand-written code generates from time to time, you might develop a better intuition for writing your code in a more efficient way. As a wise man once said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Everything is vague to a degree you do not realize till you have tried to make it precise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(I’m sure I’ve read that somewhere before.)&lt;/p&gt;

&lt;p&gt;We haven’t yet discussed the third stage of running a Python program. We’ve got as far as the bytecode, but how does it get executed? The Python VM &lt;strong&gt;interprets&lt;/strong&gt; it, reading it line-by-line and executing equivalent instructions on the physical machine you’re running it on.&lt;/p&gt;

&lt;p&gt;We saw how you can find out exactly how the first two steps of the process work. The third step is no different, but it’s not so easy! You would have to &lt;a href="https://github.com/python/cpython" rel="noopener noreferrer"&gt;read the CPython source code&lt;/a&gt; and understand how the CPython VM works. I think I’ll leave that one for another time.&lt;/p&gt;

&lt;h2&gt;
  
  
  But seriously…
&lt;/h2&gt;

&lt;p&gt;If you’re intersted in understanding the inner workings of Python, you could get involved with CPython development. It’s an extremely well-managed open-source project with &lt;a href="https://devguide.python.org/" rel="noopener noreferrer"&gt;a very comprehensive developer guide&lt;/a&gt;. A great way to get started is to try fixing something from the list of &lt;a href="https://devguide.python.org/fixingissues/" rel="noopener noreferrer"&gt;‘easy’ issues&lt;/a&gt;- that’ll still be a challenge but they’re picked out to be &lt;em&gt;possible&lt;/em&gt; to figure out as a beginner.&lt;/p&gt;

&lt;p&gt;Another way to contribute to core Python is to work on a compiler other than the CPython reference implementation. These projects typically have more room to make your mark than something as giant as CPython. A good example is the Python-to-JavaScript compiler named &lt;a href="https://github.com/skulpt/skulpt" rel="noopener noreferrer"&gt;Skuplt&lt;/a&gt;, used by Anvil to run Python in the web browser. Skulpt is an active open-source project that welcomes new contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/skulpt/skulpt#how-can-i-help" rel="noopener noreferrer"&gt;Contribute to Skulpt &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;And if you want to see Skulpt in action, try building something in Anvil:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://anvil.works/build" rel="noopener noreferrer"&gt;Build something in Anvil &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h2&gt;




&lt;p&gt;&lt;a id="fn-footnotes-dict"&gt;[1]:&lt;/a&gt; Incidentally, this code did not convert my object to a dictionary, the dictionary already exists and it’s where Python stores the object’s attributes. Retrieving values from Python dictionaries is really fast, so they are used as the mechanism for getting attributes from objects. &lt;sup&gt;return&lt;/sup&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a id="fn-footnotes-requests"&gt;[2]:&lt;/a&gt; The documentation of &lt;code&gt;requests&lt;/code&gt; is actually very good, and the &lt;code&gt;requests&lt;/code&gt; library itself is a work of genius. In fact, API is so well-designed that it makes a neat example of learning by introspection, which is why I’ve used it here! &lt;sup&gt;[return]&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>oop</category>
    </item>
    <item>
      <title>Generate PDFs with Python using Anvil</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Fri, 13 Nov 2020 10:56:32 +0000</pubDate>
      <link>https://dev.to/meredydd/generating-pdfs-with-python-and-anvil-4fcf</link>
      <guid>https://dev.to/meredydd/generating-pdfs-with-python-and-anvil-4fcf</guid>
      <description>&lt;h1&gt;
  
  
  PDFs Made Easy
&lt;/h1&gt;

&lt;p&gt;Generating PDF documents in Python can be a pain, with lots of janky dependencies and HTML generation. I'm going to show you a much easier way with nothing but Python!&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://anvil.works" rel="noopener noreferrer"&gt;Anvil&lt;/a&gt;, the pure-Python full-stack web app tool, &lt;a href="https://anvil.works/docs/media/creating_pdfs" rel="noopener noreferrer"&gt;generating a PDF document&lt;/a&gt; is easy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Design a page in Anvil's &lt;strong&gt;drag-and-drop designer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Render it to PDF with one function call&lt;/li&gt;
&lt;li&gt;Download it in the browser, or send it by email, or store it in a database, or...&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this tutorial, we'll build a simple web app where users can register for an event and download their ticket as a PDF. Follow along with the tutorial, or clone and try out the finished app:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/build#clone:3GTU3RKLSYDYSHUV=FSKRYZVOYLAGKZGXGBFBM2HQ" rel="noopener noreferrer"&gt;Open the App in Anvil &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h3&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-0-new.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-0-new.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Completely new to Anvil? You might want to try one of our &lt;a href="https://anvil.works/learn/tutorials#starthere" rel="noopener noreferrer"&gt;'Start Here' tutorials&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1 - Design your PDF
&lt;/h2&gt;

&lt;p&gt;First, let's design a Form that we want to convert to a PDF ticket. Add a new Blank Panel Form to the app and rename it 'Ticket'. &lt;/p&gt;

&lt;p&gt;We can design our Form however we like. To make the Form look like a ticket, I've added a Card with a dotted border and a Label that says 'ADMIT ONE'. We should also add Labels to display the user's name (&lt;code&gt;name_label&lt;/code&gt;) and the date of the event (&lt;code&gt;date_label&lt;/code&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-1-new.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-1-new.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We should change the &lt;code&gt;__init__&lt;/code&gt; of the Ticket Form so that it takes strings called &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;date&lt;/code&gt; as arguments and displays them on the labels we just created. Switch to Code View in the Form Editor and change the &lt;code&gt;__init__&lt;/code&gt; function of our Ticket class so it looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TicketTemplate&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Set Form properties and Data Bindings.
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2 - Create the user interface
&lt;/h2&gt;

&lt;p&gt;Let's now build a UI so that users can generate and download the ticket as a PDF. We'll design a simple registration page for users to put in their name and email address and choose the date of the event they will attend. &lt;/p&gt;

&lt;p&gt;Switch to Form1 in the App Browser. We need two TextBoxes so users can fill in their name and email address and two Labels for these. We'll name the TextBoxes &lt;code&gt;name_box&lt;/code&gt; and &lt;code&gt;email_box&lt;/code&gt;. We'll also need a DropDown named &lt;code&gt;date_dropdown&lt;/code&gt; so that users can choose the date of the event.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-2-1-new.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-2-1-new.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We should populate the &lt;strong&gt;items&lt;/strong&gt; property of our DropDown with a few dates. Each item in the DropDown should be on a separate line. We can also include a placeholder that says 'choose date'.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-2-2.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-2-2.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's add a Button called &lt;code&gt;register_button&lt;/code&gt;, so users can register and download their PDF ticket. Let's add a &lt;code&gt;click&lt;/code&gt; event for this Button that just displays our PDF form, so we can see what it will look like when it's downloaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_button_click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;event_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;This method is called when the button is clicked&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_value&lt;/span&gt;
    &lt;span class="nf"&gt;open_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ticket&lt;/span&gt;&lt;span class="sh"&gt;'&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run the app! Fill in a name, select a date and click REGISTER to see with the ticket looks like.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-2-3.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-2-3.gif"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 - Render and download the PDF
&lt;/h2&gt;

&lt;p&gt;Our ticket is looking good! Now let's turn it into a PDF. Rather than displaying the Ticket form in the browser, we'll define a server function to render it as a PDF document.&lt;/p&gt;

&lt;p&gt;We can only generate a PDF from &lt;a href="https://anvil.works/docs/server" rel="noopener noreferrer"&gt;Server code&lt;/a&gt;, so we'll first need to add a Server Module to our app. We define a function which calls &lt;code&gt;anvil.pdf.render_form()&lt;/code&gt;. This method returns a &lt;a href="https://anvil.works/docs/media" rel="noopener noreferrer"&gt;Media object&lt;/a&gt;, which means we can pass it back to the browser, download it or send it as an email attachment. We also need to decorate our fuction with &lt;code&gt;@anvil.server.callable&lt;/code&gt; so we can call the function from client code.&lt;/p&gt;

&lt;p&gt;Here's what our server code looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anvil.pdf&lt;/span&gt;

&lt;span class="nd"&gt;@anvil.server.callable&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_pdf&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ticket&lt;/span&gt;&lt;span class="sh"&gt;'&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's call this function from the registration form then download the generated PDF. We use &lt;code&gt;anvil.server.call()&lt;/code&gt; to call our server function and generate the PDF, and &lt;code&gt;anvil.media.download()&lt;/code&gt; to save it. The &lt;code&gt;click&lt;/code&gt; event should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_button_click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;event_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;This method is called when the button is clicked&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_value&lt;/span&gt;

    &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create_pdf&lt;/span&gt;&lt;span class="sh"&gt;'&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all we need to do to render a Form as a PDF. And we can download it with just one line of code!&lt;/p&gt;

&lt;p&gt;Let's test it out. Run the app again, fill in the form and click REGISTER. This time, the ticket will download.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-3-1.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-3-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - Send your PDF as an email attachment
&lt;/h2&gt;

&lt;p&gt;Once we've rendered our Form as a PDF, it's easy to send an email with the document attached.&lt;/p&gt;

&lt;p&gt;We first need to add the &lt;a href="https://anvil.works/docs/email" rel="noopener noreferrer"&gt;Email Service&lt;/a&gt; to our app. In the App Browser, click the + next to SERVICES and click on Email. To test out the app, we can check Test Mode so that all emails are redirected to us.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-4-1.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-4-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can then write a function in the Server Module which calls &lt;code&gt;create_pdf&lt;/code&gt; and then sends the rendered PDF as an email attachment:&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="nd"&gt;@anvil.server.callable&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_pdf_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_pdf&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;from_address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;no-reply&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;from_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;Events&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Your Ticket&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Thanks for registering!. Your ticket is attached to this email.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To avoid being used for Spam, by default &lt;code&gt;anvil.email.send()&lt;/code&gt; sends all email to the app owner (ie you) if you're using the Free plan. You can deliver email to other people by &lt;a href="https://anvil.works/docs/email/sending_and_receiving#custom-mail-server" rel="noopener noreferrer"&gt;configuring an SMTP server in the Email service&lt;/a&gt; (SendGrid's Free plan works great for this), or by upgrading to a paid Anvil plan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let's modify the &lt;code&gt;register_button_click&lt;/code&gt; method in Form1 so that it calls the &lt;code&gt;send_pdf_email&lt;/code&gt; function we just wrote. Since &lt;code&gt;send_pdf_email&lt;/code&gt; returns the rendered PDF, we just need to swap it with &lt;code&gt;create_pdf&lt;/code&gt; and pass in &lt;code&gt;email&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_button_click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;event_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;This method is called when the button is clicked&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_value&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;

    &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;send_pdf_email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now run the app again and our PDF ticket will be emailed to us. As long as we are in Test Mode, the ticket will always be emailed us no matter what email address we type in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 - Check the input
&lt;/h2&gt;

&lt;p&gt;We want to make sure that the user has actually filled in the registration form, so let's add in a check before we generate the PDF ticket. We'll alert the user if they haven't filled the form, but if they have, we'll tell them that their ticket will be downloaded and emailed to them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_button_click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;event_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;This method is called when the button is clicked&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected_value&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Thanks for registering! Your ticket is downloading and will be sent to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="si"&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="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;send_pdf_email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;You have not completed all required fields&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try running the app again, but this time, leave at least one of the TextBoxes blank.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-5.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-5.gif"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 - Optional: Set the PDF filename
&lt;/h2&gt;

&lt;p&gt;Our app now generates, downloads and emails a PDF attachment when a user registers for our event. However, the PDF is called 'print.pdf' when we download it, which isn't very descriptive. If we want to specify settings for our PDF document, we can use &lt;a href="https://anvil.works/docs/media/creating_pdfs#customising-settings" rel="noopener noreferrer"&gt;&lt;code&gt;PDFRender()&lt;/code&gt;&lt;/a&gt;. Let's modify our &lt;code&gt;create_pdf&lt;/code&gt; function and specify a filename using &lt;code&gt;PDFRenderer()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First add &lt;code&gt;from anvil.pdf import PDFRenderer&lt;/code&gt; to the top of the Server code then modify the &lt;code&gt;create_pdf&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anvil.pdf&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;anvil.pdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PDFRenderer&lt;/span&gt;

&lt;span class="nd"&gt;@anvil.server.callable&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_pdf&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PDFRenderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Ticket.pdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ticket&lt;/span&gt;&lt;span class="sh"&gt;'&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when our ticket is downloaded, it will have a more useful name.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7 - Run the finished app
&lt;/h2&gt;

&lt;p&gt;We've now created a simple app which converts an Anvil form to a downloadable PDF and sends it as an email attachment. Let's test out the finished app.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-0-new.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fpdfs%2Fpdfs-0-new.gif"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  That's it!
&lt;/h2&gt;

&lt;p&gt;Your app is already live on the internet. Go to &lt;a href="https://anvil.works/docs/deployment" rel="noopener noreferrer"&gt;Publish App&lt;/a&gt; in the Gear Menu for details.&lt;/p&gt;

&lt;p&gt;You can check out the finished app here:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/build#clone:3GTU3RKLSYDYSHUV=FSKRYZVOYLAGKZGXGBFBM2HQ" rel="noopener noreferrer"&gt;Open the Finished App in Anvil &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;If you thought this was cool, check out the rest of Anvil! &lt;/p&gt;

&lt;p&gt;Head to the &lt;a href="https://anvil.works/learn/tutorials" rel="noopener noreferrer"&gt;Anvil Learning Centre&lt;/a&gt; for more tutorials, or head to our &lt;a href="https://anvil.works/learn/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; page to see how to build some complex apps in Anvil.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/learn" rel="noopener noreferrer"&gt;More Anvil Tutorials &amp;gt;&amp;gt;&lt;/a&gt;
&lt;/h3&gt;

</description>
      <category>python</category>
      <category>pdf</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploy a Machine Learning Model as a Web App with Anvil and Deepnote</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Fri, 06 Nov 2020 17:25:34 +0000</pubDate>
      <link>https://dev.to/meredydd/deploy-a-machine-learning-model-as-a-web-app-with-anvil-and-deepnote-k4k</link>
      <guid>https://dev.to/meredydd/deploy-a-machine-learning-model-as-a-web-app-with-anvil-and-deepnote-k4k</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Prefer watching to reading? This tutorial is also on YouTube:&lt;/em&gt;&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0ppptVxgEI8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Deploying Data Science with nothing but Python
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://deepnote.com" rel="noopener noreferrer"&gt;Deepnote&lt;/a&gt; is a seriously slick Python notebook, hosted in the cloud, with incredible real-time collaboration. It's great for working with other data scientists. But what happens when you want to share your project with non-programmers? They need an easy-to-use interface -- so you need to &lt;strong&gt;deploy what you've built&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://anvil.works" rel="noopener noreferrer"&gt;Anvil&lt;/a&gt; lets you build full-featured web apps entirely in Python (no HTML or JS required) -- and you can connect it to external notebooks.&lt;/p&gt;

&lt;p&gt;Follow along as we use Deepnote and Anvil to deploy a machine learning model as a web app -- a web app anyone can use!&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fdeepnote-demo-app.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fdeepnote-demo-app.gif" alt="The app we're going to build"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;We're going to build this app -- and it's going to be easy!&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 0 - Get the notebook
&lt;/h2&gt;

&lt;p&gt;In this example, we're going to start with an image classifier that can tell the difference between cats and dogs. Believe it or not, getting this notebook running in the cloud is the easy part! Just open the notebook in Deepnote and click &lt;strong&gt;Duplicate&lt;/strong&gt;:&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://deepnote.com/project/b3979913-ea08-4cd2-92fb-0b9345492bf1" rel="noopener noreferrer"&gt;&lt;strong&gt;Open the notebook &amp;gt;&amp;gt;&lt;/strong&gt;&lt;/a&gt;
&lt;/h3&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fdeepnote-clone.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fdeepnote-clone.png" alt="Deepnote screenshot"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Click “Duplicate” to make a copy of this notebook in your own (free) Deepnote account.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All right, we have a notebook. &lt;strong&gt;Let's deploy it as a web app!&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1 - Create your Anvil app
&lt;/h2&gt;

&lt;p&gt;Creating web apps with Anvil is simple. No need to wrestle with HTML, CSS, JavaScript or PHP -- we can do everything in Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://anvil.works/login" rel="noopener noreferrer"&gt;Log in&lt;/a&gt; to Anvil and click 'New Blank App'. Choose the Material Design theme.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Ffeedback-form%2Fcreate-app.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Ffeedback-form%2Fcreate-app.png" alt="Location of the Create App button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, name the app. Click on the name at the top of the screen and give it a name:&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Frename-app.png" alt="Rename your app by clicking on the title"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Step 2 - Add your components
&lt;/h2&gt;

&lt;p&gt;We construct the UI by dragging-and-dropping &lt;a href="https://anvil.works/docs/client/components" rel="noopener noreferrer"&gt;components&lt;/a&gt; from the &lt;a href="https://anvil.works/docs/editor#toolbox" rel="noopener noreferrer"&gt;Toolbox&lt;/a&gt;. Drop a &lt;strong&gt;Card&lt;/strong&gt; and a &lt;strong&gt;FileLoader&lt;/strong&gt;. The FileLoader will let users upload the image they want classified. &lt;/p&gt;

&lt;p&gt;Next, add an &lt;strong&gt;Image&lt;/strong&gt; component, which will display the input image, and a &lt;strong&gt;Label&lt;/strong&gt; to display the returned classification.&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fdrag-drop-images-classifier.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fdrag-drop-images-classifier.gif" alt="Anvil Drag and Drop demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;code&gt;Label&lt;/code&gt; we just added and, in the components properties, change the name to 'results_lbl'. Then add a spacer component above the label to centre the label against the image.&lt;/p&gt;

&lt;p&gt;Now we have a user interface, let's connect our app to the code in our Jupyter notebook.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3 - Enable the Uplink
&lt;/h2&gt;

&lt;p&gt;From the IDE, let's enable the Uplink. Open the Gear menu in the top left of the IDE, then select &lt;code&gt;Uplink&lt;/code&gt; and then &lt;code&gt;Enable the Anvil Server Uplink&lt;/code&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fuplink-activation.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fuplink-activation.gif" alt="Enable the Uplink via the gear at the top of the left panel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will then give us an Uplink key we can use in our Deepnote notebook. &lt;/p&gt;

&lt;p&gt;Now let's install the Uplink in our Deepnote environment, and connect our script using the key we just created.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 4 - Install the Uplink Library in our Deepnote notebook
&lt;/h2&gt;

&lt;p&gt;It's time to connect our notebook to Anvil!&lt;/p&gt;

&lt;p&gt;The first thing we need to do is install the &lt;code&gt;anvil-uplink&lt;/code&gt; library. Let's add &lt;code&gt;!pip install anvil-uplink&lt;/code&gt; to our notebook:&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;!&lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;anvil-uplink
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;!&lt;/code&gt; operator tells our notebook that this line is a command line script and not Python code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 - Connecting our Script
&lt;/h2&gt;

&lt;p&gt;Now that the Uplink library will be installed when we start our notebook, we can connect our notebook in the same way as any other Uplink script.&lt;/p&gt;

&lt;p&gt;Start by importing the &lt;code&gt;anvil.server&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anvil.server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then connect to the Uplink:&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;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-uplink-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace "your-uplink-key" with the Uplink key from your app. &lt;/p&gt;

&lt;p&gt;Run the cell. You should see output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connecting to wss://anvil.works/uplink
Anvil websocket open
Authenticated OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! When we run our notebook, it will now connect to our web app via the Uplink. Next, let's create a function we can call from our Anvil app.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 - Creating a callable function
&lt;/h2&gt;

&lt;p&gt;In a new cell, let's define a function we can call from our app to classify an image. We'll call it &lt;code&gt;classify_image&lt;/code&gt;, and decorate it with &lt;code&gt;@anvil.server.callable&lt;/code&gt; so we can call it from the web.&lt;/p&gt;

&lt;p&gt;The image will be passed to our &lt;code&gt;classify_image&lt;/code&gt; function as an Anvil &lt;a href="https://anvil.works/docs/media" rel="noopener noreferrer"&gt;media object&lt;/a&gt;. We'll write it to a temporary file and load it into it into &lt;a href="https://pillow.readthedocs.io/en/stable/#" rel="noopener noreferrer"&gt;Pillow&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Our notebook already has a function, &lt;code&gt;get_prediction()&lt;/code&gt;, for putting the Pillow image through the classifier. We'll call that function and return the results in two parts: the numerical score, and then the classification. We'll say anything scoring less than 0.5 is a 'dog', and anything else is a 'cat'.&lt;/p&gt;

&lt;p&gt;Here is how our finished cell should look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anvil.media&lt;/span&gt;

&lt;span class="nd"&gt;@anvil.server.callable&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TempFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&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;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_img&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;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_prediction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dog&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our notebook is now ready to classify images! Let's go back into our Anvil app and call that &lt;code&gt;classify_image&lt;/code&gt; function when someone uploads an image.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7 - Calling notebook functions from the web
&lt;/h2&gt;

&lt;p&gt;In the design view of our Anvil app, double click the FileLoader component we added earlier. It will take us to the code view and add a function that runs whenever a file is uploaded. &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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Ffileloader-event.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Ffileloader-event.gif" alt="Adding a FileLoader change event"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the function, type &lt;code&gt;anvil.server.callable()&lt;/code&gt; and pass it the name of our notebook function &lt;code&gt;'classify_image'&lt;/code&gt;. Then pass it the uploaded &lt;code&gt;file&lt;/code&gt; as the argument to our notebook function. (Anvil will take care of transmitting the data.)&lt;/p&gt;

&lt;p&gt;We can then create two variables, &lt;code&gt;score&lt;/code&gt; and &lt;code&gt;cls&lt;/code&gt;, and set them to the two values returned by &lt;code&gt;classify_image&lt;/code&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Ffileloader_event__server_call.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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Ffileloader_event__server_call.png" alt="Calling the server"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8 - Displaying our classification
&lt;/h2&gt;

&lt;p&gt;We've got the results -- it's time to show them on the screen!&lt;/p&gt;

&lt;p&gt;Still in our &lt;code&gt;file_loader_1_change&lt;/code&gt; function, let's display the classification as text on our Label, and display the uploaded image on the Image component.&lt;/p&gt;

&lt;p&gt;We set the text of &lt;code&gt;results_lbl&lt;/code&gt; to include both the classification and the score returned by our model:&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results_lbl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then set the source of &lt;code&gt;image_1&lt;/code&gt; to the uploaded file:&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The finished function will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;file_loader_1_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;event_args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;This method is called when a new file is loaded into this FileLoader&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anvil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;classify_image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result_lbl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have an app that takes an image, sends it to our Deepnote notebook, classifies it and returns the results to our app. It's all in the cloud, and all in Python!&lt;/p&gt;

&lt;p&gt;All we need to do now is deploy our app to the internet for everyone to use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9 - Publishing our app
&lt;/h2&gt;

&lt;p&gt;From the IDE, open the Gear menu in the top left of the IDE, then select &lt;code&gt;Publish app&lt;/code&gt; and then &lt;code&gt;Share via public link&lt;/code&gt;. Choose a URL and click "Apply":&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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fpublish-app.gif" 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%2Fanvil.works%2Flearn%2Ftutorials%2Fimg%2Fdeepnote-to-web-app%2Fpublish-app.gif" alt="Publish your app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it -- we've deployed a machine learning model as a web app, with Deepnote hosting the notebook and Anvil hosting the web app. Best of all, we didn't need anything but Python!&lt;/p&gt;




&lt;h2&gt;
  
  
  Clone the App
&lt;/h2&gt;

&lt;p&gt;You can open the source code for the finished Anvil app here:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://anvil.works/build#clone:WCPMOPRJTTTBW2AQ=X3D2ITSI5ZXBHSHDL3VYR6KY" rel="noopener noreferrer"&gt;&lt;strong&gt;Clone the finished Anvil app &amp;gt;&amp;gt;&lt;/strong&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;And you can duplicate our Deepnote notebook from the link below:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://deepnote.com/project/b3979913-ea08-4cd2-92fb-0b9345492bf1" rel="noopener noreferrer"&gt;&lt;strong&gt;Open the finished notebook &amp;gt;&amp;gt;&lt;/strong&gt;&lt;/a&gt;
&lt;/h3&gt;




&lt;h2&gt;
  
  
  New to Anvil?
&lt;/h2&gt;

&lt;p&gt;If you haven't seen it before, &lt;a href="https://anvil.works/" rel="noopener noreferrer"&gt;Anvil&lt;/a&gt; makes web development accessible: Build fully featured webs apps painlessly, without JavaScript, HTML or CSS - &lt;strong&gt;just Python&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The runtime platform is open source, and the online IDE is free, so you can try it out at &lt;a href="https://anvil.works" rel="noopener noreferrer"&gt;https://anvil.works&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>datascience</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why We Open Sourced the Anvil App Server</title>
      <dc:creator>Meredydd Luff</dc:creator>
      <pubDate>Wed, 20 May 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/meredydd/why-we-open-sourced-the-anvil-app-server-565n</link>
      <guid>https://dev.to/meredydd/why-we-open-sourced-the-anvil-app-server-565n</guid>
      <description>&lt;h1&gt;
  
  
  Why Open Source?
&lt;/h1&gt;

&lt;p&gt;A little while ago, we open-sourced the &lt;a href="https://anvil.works/blog/open-source"&gt;Anvil App Server&lt;/a&gt;. The community reaction has been great, even a little overwhelming (yes, every reply to that announcement really did land in my inbox!). We’ve already incorporated a bunch of that feedback into our &lt;a href="https://github.com/anvil-works/anvil-runtime"&gt;version 1.2 release&lt;/a&gt;. But we keep getting asked, “ &lt;strong&gt;Why did you open-source such a core part of your product?&lt;/strong&gt; “&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do this at all?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://anvil.works"&gt;Anvil&lt;/a&gt; is a tool that makes it as simple as possible to build a web app. We do that by enabling you to build the whole application in one language, Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g1t4oNHo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anvil.works/blog/img/why-open-source/frameworks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g1t4oNHo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anvil.works/blog/img/why-open-source/frameworks.png" alt="Yes, really. All of these, for a simple web app."&gt;&lt;/a&gt;&lt;br&gt;
                &lt;p&gt;Yes, really. All of these, for a simple web app.&lt;/p&gt;

 &lt;/p&gt;

&lt;p&gt;If you want to build a web application today, it’s a mess. Even for the simplest application, you’ll need to know HTML, Javascript, CSS, Python, SQL, React, Redux, Bootstrap, Sass, Webpack…and that’s a &lt;em&gt;simple&lt;/em&gt; app. (Trust me, it gets &lt;a href="https://github.com/kamranahmedse/developer-roadmap#introduction"&gt;way worse&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;But even then, you’re not done! You need to know all about Git, about AWS, about how to secure a Linux system, how to set up a database…and then you’re on call to keep it all running. Forever.&lt;/p&gt;

&lt;p&gt;Or…you could use Anvil. Build your UI with a &lt;a href="https://anvil.works/docs/client/ui"&gt;drag-and-drop designer&lt;/a&gt;, write all your &lt;a href="https://anvil.works/docs/client/python"&gt;logic in Python&lt;/a&gt;, and we’ll take care of the rest. We replace that whole teetering stack with “just write Python”. (Intrigued? &lt;a href="https://anvil.works/learn"&gt;We’ve got tutorials&lt;/a&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple hosting is part of that.
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The web platform is broken&lt;/strong&gt; , and Anvil is our attempt to fix it. And, because there is so much complexity in deploying a web app, a cloud-hosted service was the best way to provide the simplicity we want. That way, you can build your app in the Anvil Editor, and make it live on the Internet with &lt;a href="https://anvil.works/docs/deployment"&gt;one click&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But we kept hearing from people who said, “that’s great, &lt;em&gt;but&lt;/em&gt;…”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;”…but I need to run this on an offshore platform without reliable internet access”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;”…but I want to package my app into an IoT device I sell”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;or simply, &lt;em&gt;”…but if I’m putting my eggs in this basket, how can I be sure I can still run my app in ten years’ time?”&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all good points! A cloud service isn’t the right solution for everyone. If we want to serve these users, there’s got to be some way for them to get their apps out of Anvil and run them locally, under their own complete control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Source: Escape hatch, not an ejector seat
&lt;/h3&gt;

&lt;p&gt;We believe in &lt;a href="https://anvil.works/blog/escape-hatches-and-ejector-seats"&gt;escape hatches, not ejector seats&lt;/a&gt;. At conferences, we sometimes get asked, “can I export this as a Flask+JS app?”. Sure, it would be &lt;em&gt;possible&lt;/em&gt; – we could generate a server package, compile the client-side Python to Javascript, and spit out a classic web app. But it would have serious drawbacks, because:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O4Vfv74---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anvil.works/blog/img/escape-hatches-ejection-seat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O4Vfv74---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://anvil.works/blog/img/escape-hatches-ejection-seat.jpg" alt="Not a comfy ride."&gt;&lt;/a&gt;&lt;br&gt;
                &lt;p&gt;Not a comfy ride.&lt;/p&gt;

 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code generation is an ejector seat.&lt;/strong&gt; Generated code is better than nothing – at least you can edit it! But the moment you edit that code, you lose all the benefits of the system that generated it. If you’re using Anvil because of its &lt;a href="https://anvil.works/docs/editor"&gt;drag-and-drop editor&lt;/a&gt; and &lt;a href="https://anvil.works/docs/client"&gt;Python in the browser&lt;/a&gt;, why should our locally-hosted users have to use vim and Javascript?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So we did it the right way:&lt;/strong&gt; we &lt;a href="https://anvil.works/blog/open-source"&gt;open-sourced Anvil’s runtime engine&lt;/a&gt;. It’s the same code that serves your app in our hosted service, and it represents your app the same way. You can edit your code with a text editor, run it, then &lt;code&gt;git push&lt;/code&gt; it back into our online IDE. It’s not an ejector seat: there’s no explosive transition. It’s an escape hatch: you can climb out, and climb right back in.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about our business model?
&lt;/h3&gt;

&lt;p&gt;Anvil was never meant to be one of those enterprise low-code systems that charge you $20/mo for each person who uses your app. &lt;strong&gt;Anvil is a developer tool.&lt;/strong&gt; The apps you develop are &lt;em&gt;yours&lt;/em&gt;. Enabling you to &lt;a href="https://www.tomshardware.com/how-to/raspberry-pi-remote-control-camera-from-web"&gt;run them on a Raspberry Pi&lt;/a&gt; is cool, but it doesn’t change our business model.&lt;/p&gt;

&lt;p&gt;We’re doing what we always were: We make and sell a &lt;a href="https://anvil.works/#1-minute-intro-video"&gt;development tool&lt;/a&gt; that makes it drastically simpler to build web applications. We provide hosting for Anvil apps. We offer the entire development-and-hosting platform on-site for &lt;a href="https://anvil.works/docs/overview/enterprise"&gt;enterprise customers&lt;/a&gt;. And, of course, we offer a free plan so that everyone can use Anvil for hobby or educational purposes – or to start building something and see how it goes.&lt;/p&gt;

&lt;p&gt;We’ve open-sourced the &lt;a href="https://anvil.works/blog/open-source"&gt;Anvil App Server&lt;/a&gt; for the (relatively small) number of people who need it, and to provide the ultimate insurance policy. Sure, you can build an app with vim and host it yourself, without even registering an account with us (we’ve got &lt;a href="https://github.com/anvil-works/anvil-runtime/blob/master/doc/creating-and-editing-apps.md"&gt;docs&lt;/a&gt; to show you how!). But the Anvil Editor is the world’s easiest way to build a web app, and our hosted service is the easiest way to deploy it – so that’s what we expect most of our users to continue doing. And you can build with confidence, knowing that the open-source code is &lt;a href="https://github.com/anvil-works/anvil-runtime"&gt;right there&lt;/a&gt; if you need it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it yourself
&lt;/h3&gt;

&lt;p&gt;If our development philosophy resonates with you, why not try Anvil yourself?&lt;/p&gt;

&lt;p&gt;Go on – it’s free, and now it’s open source too:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://anvil.works/build"&gt;Try Anvil&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://anvil.works/learn"&gt;Read a tutorial&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
